<!DOCTYPE HTML>
<html lang="en">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="AverageJoe&#39;s Blog">
    <meta name="description" content="this is my secret garden where I sow my inspirations.">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>AverageJoe&#39;s Blog</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery.min.js"></script>
<meta name="generator" content="Hexo 5.0.0"><link rel="alternate" href="/atom.xml" title="AverageJoe's Blog" type="application/atom+xml">
</head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">AverageJoe&#39;s Blog</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>Index</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>Tags</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>Categories</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>Archives</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>About</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>Contact</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>Friends</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="Search" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">AverageJoe&#39;s Blog</div>
        <div class="logo-desc">
            
            this is my secret garden where I sow my inspirations.
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			Index
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			Tags
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			Categories
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			Archives
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			About
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			Contact
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			Friends
		</a>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    
<script src="/libs/cryptojs/crypto-js.min.js"></script>
<script>
    (function() {
        let pwd = '';
        if (pwd && pwd.length > 0) {
            if (pwd !== CryptoJS.SHA256(prompt('请输入访问本文章的密码')).toString(CryptoJS.enc.Hex)) {
                alert('密码错误，将返回主页！');
                location.href = '/';
            }
        }
    })();
</script>




<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/0.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title"></h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        margin: 35px 0 15px 0;
        padding-left: 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        height: calc(100vh - 250px);
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                          <div class="article-tag">
                            <span class="chip bg-color">No tag</span>
                          </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>Publish Date:&nbsp;&nbsp;
                    2018-08-19
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>Update Date:&nbsp;&nbsp;
                    2018-08-19
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>Word Count:&nbsp;&nbsp;
                    16.6k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>Read Times:&nbsp;&nbsp;
                    72 Min
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>Read Count:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">
        <div class="card-content article-card-content">
            <div id="articleContent">
                <h1 id="0-学习目标"><a href="#0-学习目标" class="headerlink" title="0.学习目标"></a>0.学习目标</h1><ul>
<li>了解商品规格数据结构设计思路</li>
<li>实现商品规格查询</li>
<li>了解SPU和SKU数据结构设计思路</li>
<li>实现商品查询</li>
<li>了解商品新增的页面实现</li>
<li>独立编写商品新增后台功能</li>
</ul>
<h1 id="1-商品规格数据结构"><a href="#1-商品规格数据结构" class="headerlink" title="1.商品规格数据结构"></a>1.商品规格数据结构</h1><p>乐优商城是一个全品类的电商网站，因此商品的种类繁多，每一件商品，其属性又有差别。为了更准确描述商品及细分差别，抽象出两个概念：SPU和SKU，了解一下：</p>
<h2 id="1-1-SPU和SKU"><a href="#1-1-SPU和SKU" class="headerlink" title="1.1.SPU和SKU"></a>1.1.SPU和SKU</h2><p>SPU：Standard Product Unit （标准产品单位） ，一组具有共同属性的商品集</p>
<p>SKU：Stock Keeping Unit（库存量单位），SPU商品集因具体特性不同而细分的每个商品</p>
<p>以图为例来看：</p>
<p><img src="assets/1526085541996.png" alt="1526085541996"></p>
<ul>
<li>本页的 华为Mate10 就是一个商品集（SPU）</li>
<li>因为颜色、内存等不同，而细分出不同的Mate10，如亮黑色128G版。（SKU）</li>
</ul>
<p>可以看出：</p>
<ul>
<li>SPU是一个抽象的商品集概念，为了方便后台的管理。</li>
<li>SKU才是具体要销售的商品，每一个SKU的价格、库存可能会不一样，用户购买的是SKU而不是SPU</li>
</ul>
<h2 id="1-2-数据库设计分析"><a href="#1-2-数据库设计分析" class="headerlink" title="1.2.数据库设计分析"></a>1.2.数据库设计分析</h2><h3 id="1-2-1-思考并发现问题"><a href="#1-2-1-思考并发现问题" class="headerlink" title="1.2.1.思考并发现问题"></a>1.2.1.思考并发现问题</h3><p>弄清楚了SPU和SKU的概念区分，接下来我们一起思考一下该如何设计数据库表。</p>
<p>首先来看SPU，大家一起思考下SPU应该有哪些字段来描述？</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">id:主键</span><br><span class="line">title：标题</span><br><span class="line">description：描述</span><br><span class="line">specification：规格</span><br><span class="line">packaging_list：包装</span><br><span class="line">after_service：售后服务</span><br><span class="line">comment：评价</span><br><span class="line">category_id：商品分类</span><br><span class="line">brand_id：品牌</span><br></pre></td></tr></table></figure>

<p>似乎并不复杂，但是大家仔细思考一下，商品的规格字段你如何填写？</p>
<p> <img src="assets/1526086539789.png" alt="1526086539789"></p>
<p>不同商品的规格不一定相同，数据库中要如何保存？</p>
<p>再看下SKU，大家觉得应该有什么字段？</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">id：主键</span><br><span class="line">spu_id：关联的spu</span><br><span class="line">price：价格</span><br><span class="line">images：图片</span><br><span class="line">stock：库存</span><br><span class="line">颜色？</span><br><span class="line">内存？</span><br><span class="line">硬盘？</span><br></pre></td></tr></table></figure>

<p>碰到难题了，不同的商品分类，可能属性是不一样的，比如手机有内存，衣服有尺码，我们是全品类的电商网站，这些不同的商品的不同属性，如何设计到一张表中？</p>
<h3 id="1-2-2-分析规格参数"><a href="#1-2-2-分析规格参数" class="headerlink" title="1.2.2.分析规格参数"></a>1.2.2.分析规格参数</h3><p>仔细查看每一种商品的规格你会发现：</p>
<p>虽然商品规格千变万化，但是同一类商品（如手机）的规格是统一的，有图为证：</p>
<blockquote>
<p>华为的规格：</p>
</blockquote>
<p> <img src="assets/1526087063700.png" alt="1526087063700"></p>
<blockquote>
<p>三星的规格：</p>
</blockquote>
<p> <img src="assets/1526087142454.png" alt="1526087142454"></p>
<p>也就是说，商品的规格参数应该是与分类绑定的。<strong>每一个分类都有统一的规格参数模板，但不同商品其参数值可能不同</strong>。</p>
<p>如下图所示：</p>
<p> <img src="assets/1526088168565.png" alt="1526088168565"></p>
<h3 id="1-2-3-SKU的特有属性"><a href="#1-2-3-SKU的特有属性" class="headerlink" title="1.2.3.SKU的特有属性"></a>1.2.3.SKU的特有属性</h3><p>SPU中会有一些特殊属性，用来区分不同的SKU，我们称为SKU特有属性。如华为META10的颜色、内存属性。</p>
<p>不同种类的商品，一个手机，一个衣服，其SKU属性不相同。</p>
<p>同一种类的商品，比如都是衣服，SKU属性基本是一样的，都是颜色、尺码等。</p>
<p>这样说起来，似乎SKU的特有属性也是与分类相关的？事实上，仔细观察你会发现，<strong>SKU的特有属性是商品规格参数的一部分</strong>：</p>
<p><img src="assets/1526088981953.png" alt="1526088981953"></p>
<p>也就是说，我们没必要单独对SKU的特有属性进行设计，它可以看做是规格参数中的一部分。这样规格参数中的属性可以标记成两部分：</p>
<ul>
<li>所有sku共享的规格属性（称为全局属性）</li>
<li>每个sku不同的规格属性（称为特有属性）</li>
</ul>
<p><img src="assets/1526089506566.png" alt="1526089506566"></p>
<h3 id="1-2-4-搜索属性"><a href="#1-2-4-搜索属性" class="headerlink" title="1.2.4.搜索属性"></a>1.2.4.搜索属性</h3><p>打开一个搜索页，我们来看看过滤的条件：</p>
<p><img src="assets/1526090072535.png" alt="1526090072535"></p>
<p>你会发现，过滤条件中的屏幕尺寸、运行内存、网路、机身内存、电池容量、CPU核数等，在规格参数中都能找到：</p>
<p> <img src="assets/1526090228171.png" alt="1526090228171"></p>
<p>也就是说，规格参数中的数据，将来会有一部分作为搜索条件来使用。我们可以在设计时，将这部分属性标记出来，将来做搜索的时候，作为过滤条件。要注意的是，无论是SPU的全局属性，还是SKU的特有属性，都有可能作为搜索过滤条件的，并不冲突，而是有一个交集：</p>
<p> <img src="assets/1526091216124.png" alt="1526091216124"></p>
<h2 id="1-3-规格参数表"><a href="#1-3-规格参数表" class="headerlink" title="1.3.规格参数表"></a>1.3.规格参数表</h2><h3 id="1-3-1-表结构"><a href="#1-3-1-表结构" class="headerlink" title="1.3.1.表结构"></a>1.3.1.表结构</h3><p>先看下规格参数表：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE &#96;tb_specification&#96; (</span><br><span class="line">  &#96;category_id&#96; bigint(20) NOT NULL COMMENT &#39;规格模板所属商品分类id&#39;,</span><br><span class="line">  &#96;specifications&#96; varchar(3000) NOT NULL DEFAULT &#39;&#39; COMMENT &#39;规格参数模板，json格式&#39;,</span><br><span class="line">  PRIMARY KEY (&#96;category_id&#96;)</span><br><span class="line">) ENGINE&#x3D;InnoDB DEFAULT CHARSET&#x3D;utf8 COMMENT&#x3D;&#39;商品规格参数模板，json格式。&#39;;</span><br></pre></td></tr></table></figure>

<p>很奇怪是吧，只有两个字段。特别需要注意的是第二个字段：</p>
<ul>
<li>specificatons：规格参数模板，json格式</li>
</ul>
<p>为什么是一个json？我们看下规格参数的格式：</p>
<p><img src="assets/1526092179381.png" alt="1526092179381"></p>
<p>如果按照传统数据库设计，这里至少需要3张表：</p>
<ul>
<li>group：代表组，与商品分类关联</li>
<li>param_key：属性名，与组关联，一对多</li>
<li>param_value：属性备选值，与属性名关联，一对多</li>
</ul>
<p>这样程序的复杂度大大增加，但是提高了数据的复用性。</p>
<p>我们的解决方案是，采用json来保存整个规格参数模板，不需要额外的表，一个字符串就够了。</p>
<h3 id="1-3-2-json结构分析"><a href="#1-3-2-json结构分析" class="headerlink" title="1.3.2.json结构分析"></a>1.3.2.json结构分析</h3><blockquote>
<p>先整体看一下：</p>
</blockquote>
<p> <img src="assets/1526092693138.png" alt="1526092693138"></p>
<ul>
<li>因为规格参数分为很多组，所以json最外层是一个数组。</li>
<li>数组中是对象类型，每个对象代表一个组的数据，对象的属性包括：<ul>
<li>group：组的名称</li>
<li>params：该组的所有属性</li>
</ul>
</li>
</ul>
<blockquote>
<p>接下来是params：</p>
</blockquote>
<p> <img src="assets/1526093111370.png" alt="1526093111370"></p>
<p>以<code>主芯片</code>这一组为例：</p>
<ul>
<li><p>group：注明，这里是主芯片</p>
</li>
<li><p>params：该组的所有规格属性，因为不止一个，所以是一个数组。这里包含四个规格属性：CPU品牌，CPU型号，CPU频率，CPU核数。每个规格属性都是一个对象，包含以下信息：</p>
<ul>
<li>k：属性名称</li>
<li>searchable：是否作为搜索字段，将来在搜索页面使用，boolean类型</li>
<li>global：是否是SPU全局属性，boolean类型。true为全局属性，false为SKU的特有属性</li>
<li>options：属性值的可选项，数组结构。起约束作用，不允许填写可选项以外的值，比如CPU核数，有人添10000核岂不是很扯淡</li>
<li>numerical：是否为数值，boolean类型，true则为数值，false则不是。为空也代表非数值</li>
<li>unit：单位，如：克，毫米。如果是数值类型，那么就需要有单位，否则可以不填。</li>
</ul>
</li>
</ul>
<p>上面的截图中所有属性都是全局属性，我们来看看内存，应该是特有属性：</p>
<p>  <img src="assets/1526262641446.png" alt="1526262641446"></p>
<p>总结下：</p>
<ul>
<li>规格参数分组，每组有多个参数</li>
<li>参数的 <code>k</code>代表属性名称，没有值，具体的SPU才能确定值</li>
<li>参数会有不同的属性：是否可搜索，是否是全局、是否是数值，这些都用boolean值进行标记：<ul>
<li>SPU下的多个SKU共享的参数称为全局属性，用<code>global</code>标记</li>
<li>SPU下的多个SKU特有的参数称为特有属性</li>
<li>如果参数是数值类型，用<code>numerical</code>标记，并且指定单位<code>unit</code></li>
<li>如果参数可搜索，用<code>searchable</code>标记</li>
</ul>
</li>
</ul>
<h1 id="2-商品规格参数管理"><a href="#2-商品规格参数管理" class="headerlink" title="2.商品规格参数管理"></a>2.商品规格参数管理</h1><h2 id="2-1-页面实现"><a href="#2-1-页面实现" class="headerlink" title="2.1.页面实现"></a>2.1.页面实现</h2><p>页面比较复杂，这里就不带着大家去实现完整页面效果了，我们一起分析一下即可。</p>
<h3 id="2-1-1-整体布局"><a href="#2-1-1-整体布局" class="headerlink" title="2.1.1.整体布局"></a>2.1.1.整体布局</h3><p>打开规格参数页面，看到如下内容：</p>
<p><img src="assets/1526095296462.png" alt="1526095296462"></p>
<p>因为规格是跟商品分类绑定的，因此首先会展现商品分类树，并且提示你要选择商品分类，才能看到规格参数的模板。一起了解下页面的实现：</p>
<p><img src="assets/1526095548672.png" alt="1526095548672"></p>
<p>可以看出页面分成3个部分：</p>
<ul>
<li><p><code>v-card-title</code>：标题部分，这里是提示信息，告诉用户要先选择分类，才能看到模板</p>
</li>
<li><p><code>v-tree</code>：这里用到的是我们之前讲过的树组件，展示商品分类树，不过现在是假数据，我们只要把<code>treeData</code>属性删除，它就会走<code>url</code>属性指定的路径去查询真实的商品分类树了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;v-tree url=<span class="string">&quot;/item/category/list&quot;</span> :isEdit=<span class="string">&quot;false&quot;</span>  @handleClick=<span class="string">&quot;handleClick&quot;</span> /&gt;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>v-dialog</code>：Vuetify提供的对话框组件，v-model绑定的dialog属性是boolean类型：</p>
<ul>
<li>true则显示弹窗</li>
<li>false则隐藏弹窗</li>
</ul>
</li>
</ul>
<h3 id="2-1-2-data中定义的属性"><a href="#2-1-2-data中定义的属性" class="headerlink" title="2.1.2.data中定义的属性"></a>2.1.2.data中定义的属性</h3><p>接下来，看看Vue实例中data定义了哪些属性，对页面会产生怎样的影响：</p>
<p> <img src="assets/1526287774316.png" alt="1526287774316"></p>
<ul>
<li>specifications：选中一个商品分类后，需要查询后台获取规格参数信息，保存在这个对象中，Vue会完成页面渲染。</li>
<li>oldSpec：当前页兼具了规格的增、改、查等功能，这个对象记录被修改前的规格参数，以防用户撤销修改，用来恢复数据。</li>
<li>dialog：是否显示对话框的标记。true则显示，false则不显示</li>
<li>currentNode：记录当前选中的商品分类节点</li>
<li>isInsert：判断接下来是新增还是修改</li>
</ul>
<h2 id="2-2-规格参数的查询"><a href="#2-2-规格参数的查询" class="headerlink" title="2.2.规格参数的查询"></a>2.2.规格参数的查询</h2><p>点击树节点后要显示规格参数，因此查询功能应该编写在点击事件中。</p>
<p>了解一下：</p>
<h3 id="2-2-1-树节点的点击事件"><a href="#2-2-1-树节点的点击事件" class="headerlink" title="2.2.1.树节点的点击事件"></a>2.2.1.树节点的点击事件</h3><p>当我们点击树节点时，要将<code>v-dialog</code>打开，因此必须绑定一个点击事件：</p>
<p> <img src="assets/1526095959539.png" alt="1526095959539"></p>
<p>我们来看下<code>handleClick</code>方法：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">handleClick(node) &#123;</span><br><span class="line">    <span class="comment">// 判断点击的节点是否是父节点（只有点击到叶子节点才会弹窗）</span></span><br><span class="line">    <span class="keyword">if</span> (!node.isParent) &#123;</span><br><span class="line">        <span class="comment">// 如果是叶子节点，那么就发起ajax请求，去后台查询商品规格数据。</span></span><br><span class="line">        <span class="built_in">this</span>.$http.get(<span class="string">&quot;/item/spec/&quot;</span> + node.id)</span><br><span class="line">            .then(<span class="function"><span class="params">resp</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="comment">// 查询成功后，把响应结果赋值给specifications属性，Vue会进行自动渲染。</span></span><br><span class="line">            <span class="built_in">this</span>.specifications = resp.data;</span><br><span class="line">            <span class="comment">// 记录下此时的规格数据，当页面撤销修改时，用来恢复原始数据</span></span><br><span class="line">            <span class="built_in">this</span>.oldSpec = resp.data;</span><br><span class="line">            <span class="comment">// 打开弹窗</span></span><br><span class="line">            <span class="built_in">this</span>.dialog = <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 标记此时要进行修改操作</span></span><br><span class="line">            <span class="built_in">this</span>.isInsert = <span class="literal">false</span>;</span><br><span class="line">        &#125;)</span><br><span class="line">            .catch(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="comment">// 如果没有查询成功，那么询问是否添加规格</span></span><br><span class="line">            <span class="built_in">this</span>.$message.confirm(<span class="string">&#x27;该分类还没有规格参数，是否添加?&#x27;</span>)</span><br><span class="line">                .then(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要添加，则将specifications初始化为空</span></span><br><span class="line">                <span class="built_in">this</span>.specifications = [&#123;</span><br><span class="line">                    group: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    params: []</span><br><span class="line">                &#125;];</span><br><span class="line">                <span class="comment">// 打开弹窗</span></span><br><span class="line">                <span class="built_in">this</span>.dialog = <span class="literal">true</span>;</span><br><span class="line">                <span class="comment">// 标记为新增</span></span><br><span class="line">                <span class="built_in">this</span>.isInsert = <span class="literal">true</span>;</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>因此，我们接下来要做的事情，就是编写接口，实现规格参数的查询了。</p>
<h3 id="2-2-2-后端代码"><a href="#2-2-2-后端代码" class="headerlink" title="2.2.2.后端代码"></a>2.2.2.后端代码</h3><blockquote>
<p>实体类</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_specification&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Specification</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="keyword">private</span> Long categoryId;</span><br><span class="line">    <span class="keyword">private</span> String specifications;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Long <span class="title">getCategoryId</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> categoryId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCategoryId</span><span class="params">(Long categoryId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.categoryId = categoryId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getSpecifications</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> specifications;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSpecifications</span><span class="params">(String specifications)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.specifications = specifications;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>mapper</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SpecificationMapper</span> <span class="keyword">extends</span> <span class="title">Mapper</span>&lt;<span class="title">Specification</span>&gt; </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>controller</p>
</blockquote>
<p>先分析下需要的东西，在页面的ajax请求中可以看出：</p>
<ul>
<li><p>请求方式：查询，肯定是get</p>
</li>
<li><p>请求路径：/spec/{cid} ，这里通过路径占位符传递商品分类的id</p>
</li>
<li><p>请求参数：商品分类id</p>
</li>
<li><p>返回结果：页面是直接把<code>resp.data</code>赋值给了specifications：</p>
<p><img src="assets/1526104087329.png" alt="1526104087329"></p>
<p>那么我们返回的应该是规格参数的字符串</p>
</li>
</ul>
<p>代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;spec&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpecificationController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SpecificationService specificationService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ResponseEntity&lt;String&gt; <span class="title">querySpecificationByCategoryId</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span></span>&#123;</span><br><span class="line">        Specification spec = <span class="keyword">this</span>.specificationService.queryById(id);</span><br><span class="line">        <span class="keyword">if</span> (spec == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ResponseEntity.ok(spec.getSpecifications());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>service:</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpecificationService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SpecificationMapper specificationMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Specification <span class="title">queryById</span><span class="params">(Long id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.specificationMapper.selectByPrimaryKey(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>页面访问测试：</p>
</blockquote>
<p>目前，我们数据库只提供了3条规格参数信息：</p>
<p> <img src="assets/1526104551227.png" alt="1526104551227"></p>
<p>我们访问：<a target="_blank" rel="noopener" href="http://api.leyou.com/api/item/spec/76">http://api.leyou.com/api/item/spec/76</a></p>
<p> <img src="assets/1526104475036.png" alt="1526104475036"></p>
<p>然后在后台系统中测试：</p>
<p><img src="assets/1526104599452.png" alt="1526104599452"></p>
<p>当我们点击一个还不存在的规格参数的商品分类：</p>
<p><img src="assets/1526104769276.png" alt="1526104769276"></p>
<h2 id="2-3-增、删、改（作业）"><a href="#2-3-增、删、改（作业）" class="headerlink" title="2.3.增、删、改（作业）"></a>2.3.增、删、改（作业）</h2><p>增删改的作业就留给大家去完成了。页面中接口都已定义，你要做的就是实现后台接口。</p>
<h1 id="3-SPU和SKU数据结构"><a href="#3-SPU和SKU数据结构" class="headerlink" title="3.SPU和SKU数据结构"></a>3.SPU和SKU数据结构</h1><p>规格确定以后，就可以添加商品了,先看下数据库表</p>
<h2 id="3-1-SPU表"><a href="#3-1-SPU表" class="headerlink" title="3.1.SPU表"></a>3.1.SPU表</h2><h3 id="3-1-1-表结构"><a href="#3-1-1-表结构" class="headerlink" title="3.1.1.表结构"></a>3.1.1.表结构</h3><p>SPU表：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE &#96;tb_spu&#96; (</span><br><span class="line">  &#96;id&#96; bigint(20) NOT NULL AUTO_INCREMENT COMMENT &#39;spu id&#39;,</span><br><span class="line">  &#96;title&#96; varchar(255) NOT NULL DEFAULT &#39;&#39; COMMENT &#39;标题&#39;,</span><br><span class="line">  &#96;sub_title&#96; varchar(255) DEFAULT &#39;&#39; COMMENT &#39;子标题&#39;,</span><br><span class="line">  &#96;cid1&#96; bigint(20) NOT NULL COMMENT &#39;1级类目id&#39;,</span><br><span class="line">  &#96;cid2&#96; bigint(20) NOT NULL COMMENT &#39;2级类目id&#39;,</span><br><span class="line">  &#96;cid3&#96; bigint(20) NOT NULL COMMENT &#39;3级类目id&#39;,</span><br><span class="line">  &#96;brand_id&#96; bigint(20) NOT NULL COMMENT &#39;商品所属品牌id&#39;,</span><br><span class="line">  &#96;saleable&#96; tinyint(1) NOT NULL DEFAULT &#39;1&#39; COMMENT &#39;是否上架，0下架，1上架&#39;,</span><br><span class="line">  &#96;valid&#96; tinyint(1) NOT NULL DEFAULT &#39;1&#39; COMMENT &#39;是否有效，0已删除，1有效&#39;,</span><br><span class="line">  &#96;create_time&#96; datetime DEFAULT NULL COMMENT &#39;添加时间&#39;,</span><br><span class="line">  &#96;last_update_time&#96; datetime DEFAULT NULL COMMENT &#39;最后修改时间&#39;,</span><br><span class="line">  PRIMARY KEY (&#96;id&#96;)</span><br><span class="line">) ENGINE&#x3D;InnoDB AUTO_INCREMENT&#x3D;208 DEFAULT CHARSET&#x3D;utf8 COMMENT&#x3D;&#39;spu表，该表描述的是一个抽象的商品，比如 iphone8&#39;;</span><br></pre></td></tr></table></figure>

<p>与我们前面分析的基本类似，但是似乎少了一些字段，比如商品描述。</p>
<p>我们做了表的垂直拆分，将SPU的详情放到了另一张表：tb_spu_detail</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE &#96;tb_spu_detail&#96; (</span><br><span class="line">  &#96;spu_id&#96; bigint(20) NOT NULL,</span><br><span class="line">  &#96;description&#96; text COMMENT &#39;商品描述信息&#39;,</span><br><span class="line">  &#96;specifications&#96; varchar(3000) NOT NULL DEFAULT &#39;&#39; COMMENT &#39;全部规格参数数据&#39;,</span><br><span class="line">  &#96;spec_template&#96; varchar(1000) NOT NULL COMMENT &#39;特有规格参数及可选值信息，json格式&#39;,</span><br><span class="line">  &#96;packing_list&#96; varchar(1000) DEFAULT &#39;&#39; COMMENT &#39;包装清单&#39;,</span><br><span class="line">  &#96;after_service&#96; varchar(1000) DEFAULT &#39;&#39; COMMENT &#39;售后服务&#39;,</span><br><span class="line">  PRIMARY KEY (&#96;spu_id&#96;)</span><br><span class="line">) ENGINE&#x3D;InnoDB DEFAULT CHARSET&#x3D;utf8;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这张表中的数据都比较大，为了不影响主表的查询效率我们拆分出这张表。</p>
<p>需要注意的是这两个字段：specifications和spec_template。</p>
<h3 id="3-1-2-spu中的规格参数"><a href="#3-1-2-spu中的规格参数" class="headerlink" title="3.1.2.spu中的规格参数"></a>3.1.2.spu中的规格参数</h3><p>前面讲过规格参数与商品分类绑定，一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数，因此我们计划是这样：</p>
<ul>
<li>SPU中保存全局的规格参数信息。</li>
<li>SKU中保存特有规格参数。</li>
</ul>
<p>以手机为例，品牌、操作系统等肯定是全局属性，内存、颜色等肯定是特有属性。</p>
<p>当你确定了一个SPU，比如小米的：红米4X</p>
<p>全局属性举例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">品牌：小米</span><br><span class="line">型号：红米4X</span><br></pre></td></tr></table></figure>

<p>特有属性举例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">颜色：[香槟金, 樱花粉, 磨砂黑]</span><br><span class="line">内存：[2G, 3G]</span><br><span class="line">机身存储：[16GB, 32GB]</span><br></pre></td></tr></table></figure>

<p>来看下我们的 表如何存储这些信息：</p>
<h4 id="3-1-2-1-specifications字段"><a href="#3-1-2-1-specifications字段" class="headerlink" title="3.1.2.1.specifications字段"></a>3.1.2.1.specifications字段</h4><p>首先是specifications，其中保存全部规格参数信息，因此也是一个json格式：</p>
<blockquote>
<p>整体来看：</p>
</blockquote>
<p> <img src="assets/1526262279963.png" alt="1526262279963"></p>
<p>整体看上去与规格参数表中的数据一样，也是一个数组，并且分组，每组下有多个参数</p>
<blockquote>
<p>展开一组来看</p>
</blockquote>
<p> <img src="assets/1526262389233.png" alt="1526262389233"></p>
<p>可以看到，与规格参数表中的模板相比，最大的区别就是，这里指定了具体的值，因为商品确定了，其参数值肯定也确定了。</p>
<blockquote>
<p>特有属性</p>
</blockquote>
<p>刚才看到的是全局属性，那么特有属性在这个字段中如何存储呢？</p>
<p> <img src="assets/1526263214087.png" alt="1526263214087"></p>
<p>我们发现特有属性也是有的，但是，注意看这里是不确定具体值的，因为特有属性只有在SKU中才能确定。这里只是保存了options，所有SKU属性的可选项。</p>
<p>在哪里会用到这个字段的值呢，商品详情页的规格参数信息中：</p>
<p> <img src="assets/1526267700765.png" alt="1526267700765"></p>
<h4 id="3-1-2-2-spec-template字段"><a href="#3-1-2-2-spec-template字段" class="headerlink" title="3.1.2.2.spec_template字段"></a>3.1.2.2.spec_template字段</h4><p>既然specifications已经包含了所有的规格参数，那么为什么又多出了一个spec_template呢？</p>
<p>里面又有哪些内容呢？</p>
<p>来看数据格式：</p>
<p> <img src="assets/1526267952827.png" alt="1526267952827"></p>
<p>可以看出，里面只保存了规格参数中的特有属性，而且格式进行了大大的简化，只有属性的key，和待选项。</p>
<p>为什么要冗余保存一份？</p>
<p>因为很多场景下我们只需要查询特有规格属性，如果放在一起，每次查询再去分离比较麻烦。</p>
<p>比如，商品详情页展示可选的规格参数时：</p>
<p>   <img src="assets/1526267828817.png" alt="1526267828817"></p>
<h2 id="3-2-SKU表"><a href="#3-2-SKU表" class="headerlink" title="3.2.SKU表"></a>3.2.SKU表</h2><h3 id="3-2-1-表结构"><a href="#3-2-1-表结构" class="headerlink" title="3.2.1.表结构"></a>3.2.1.表结构</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE &#96;tb_sku&#96; (</span><br><span class="line">  &#96;id&#96; bigint(20) NOT NULL AUTO_INCREMENT COMMENT &#39;sku id&#39;,</span><br><span class="line">  &#96;spu_id&#96; bigint(20) NOT NULL COMMENT &#39;spu id&#39;,</span><br><span class="line">  &#96;title&#96; varchar(255) NOT NULL COMMENT &#39;商品标题&#39;,</span><br><span class="line">  &#96;images&#96; varchar(1000) DEFAULT &#39;&#39; COMMENT &#39;商品的图片，多个图片以‘,’分割&#39;,</span><br><span class="line">  &#96;price&#96; bigint(15) NOT NULL DEFAULT &#39;0&#39; COMMENT &#39;销售价格，单位为分&#39;,</span><br><span class="line">  &#96;indexes&#96; varchar(100) COMMENT &#39;特有规格属性在spu属性模板中的对应下标组合&#39;,</span><br><span class="line">  &#96;own_spec&#96; varchar(1000) COMMENT &#39;sku的特有规格参数，json格式，反序列化时应使用linkedHashMap，保证有序&#39;,</span><br><span class="line">  &#96;enable&#96; tinyint(1) NOT NULL DEFAULT &#39;1&#39; COMMENT &#39;是否有效，0无效，1有效&#39;,</span><br><span class="line">  &#96;create_time&#96; datetime NOT NULL COMMENT &#39;添加时间&#39;,</span><br><span class="line">  &#96;last_update_time&#96; datetime NOT NULL COMMENT &#39;最后修改时间&#39;,</span><br><span class="line">  PRIMARY KEY (&#96;id&#96;),</span><br><span class="line">  KEY &#96;key_spu_id&#96; (&#96;spu_id&#96;) USING BTREE</span><br><span class="line">) ENGINE&#x3D;InnoDB DEFAULT CHARSET&#x3D;utf8 COMMENT&#x3D;&#39;sku表,该表表示具体的商品实体,如黑色的64GB的iphone 8&#39;;</span><br></pre></td></tr></table></figure>

<p>还有一张表，代表库存：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE &#96;tb_stock&#96; (</span><br><span class="line">  &#96;sku_id&#96; bigint(20) NOT NULL COMMENT &#39;库存对应的商品sku id&#39;,</span><br><span class="line">  &#96;seckill_stock&#96; int(9) DEFAULT &#39;0&#39; COMMENT &#39;可秒杀库存&#39;,</span><br><span class="line">  &#96;seckill_total&#96; int(9) DEFAULT &#39;0&#39; COMMENT &#39;秒杀总数量&#39;,</span><br><span class="line">  &#96;stock&#96; int(9) NOT NULL COMMENT &#39;库存数量&#39;,</span><br><span class="line">  PRIMARY KEY (&#96;sku_id&#96;)</span><br><span class="line">) ENGINE&#x3D;InnoDB DEFAULT CHARSET&#x3D;utf8 COMMENT&#x3D;&#39;库存表，代表库存，秒杀库存等信息&#39;;</span><br></pre></td></tr></table></figure>

<p>问题：为什么要将库存独立一张表？</p>
<p>因为库存字段写频率较高，而SKU的其它字段以读为主，因此我们将两张表分离，读写不会干扰。</p>
<p>特别需要注意的是sku表中的<code>indexes</code>字段和<code>own_spec</code>字段。sku中应该保存特有规格参数的值，就在这两个字段中。</p>
<h3 id="3-2-2-sku中的特有规格参数"><a href="#3-2-2-sku中的特有规格参数" class="headerlink" title="3.2.2.sku中的特有规格参数"></a>3.2.2.sku中的特有规格参数</h3><h4 id="3-2-2-1-indexes字段"><a href="#3-2-2-1-indexes字段" class="headerlink" title="3.2.2.1.indexes字段"></a>3.2.2.1.indexes字段</h4><p>在SPU表中，已经对特有规格参数及可选项进行了保存，结构如下：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;机身颜色&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;香槟金&quot;</span>,</span><br><span class="line">        <span class="string">&quot;樱花粉&quot;</span>,</span><br><span class="line">        <span class="string">&quot;磨砂黑&quot;</span></span><br><span class="line">    ],</span><br><span class="line">    <span class="attr">&quot;内存&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;2GB&quot;</span>,</span><br><span class="line">        <span class="string">&quot;3GB&quot;</span></span><br><span class="line">    ],</span><br><span class="line">    <span class="attr">&quot;机身存储&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;16GB&quot;</span>,</span><br><span class="line">        <span class="string">&quot;32GB&quot;</span></span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这些特有属性如果排列组合，会产生12个不同的SKU，而不同的SKU，其属性就是上面备选项中的一个。</p>
<p>比如：</p>
<ul>
<li>红米4X，香槟金，2GB内存，16GB存储</li>
<li>红米4X，磨砂黑，2GB内存，32GB存储</li>
</ul>
<p>你会发现，每一个属性值，对应于SPUoptions数组的一个选项，如果我们记录下角标，就是这样：</p>
<ul>
<li>红米4X，0,0,0</li>
<li>红米4X，2,0,1</li>
</ul>
<p>既然如此，我们是不是可以将不同角标串联起来，作为SPU下不同SKU的标示。这就是我们的indexes字段。</p>
<p> <img src="assets/1526266901335.png" alt="1526266901335"></p>
<p>这个设计在商品详情页会特别有用：</p>
<p> <img src="assets/1526267180997.png" alt="1526267180997"></p>
<p>当用户点击选中一个特有属性，你就能根据 角标快速定位到sku。</p>
<h4 id="3-2-2-2-own-spec字段"><a href="#3-2-2-2-own-spec字段" class="headerlink" title="3.2.2.2.own_spec字段"></a>3.2.2.2.own_spec字段</h4><p>看结构：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">&quot;机身颜色&quot;</span>:<span class="string">&quot;香槟金&quot;</span>,<span class="attr">&quot;内存&quot;</span>:<span class="string">&quot;2GB&quot;</span>,<span class="attr">&quot;机身存储&quot;</span>:<span class="string">&quot;16GB&quot;</span>&#125;</span><br></pre></td></tr></table></figure>

<p>保存的是特有属性的键值对。</p>
<p>SPU中保存的是可选项，但不确定具体的值，而SKU中的保存的就是具体的键值对了。</p>
<p>这样，在页面展示规格参数信息时，就可以根据key来获取值，用于显示。</p>
<h2 id="3-3-导入图片信息"><a href="#3-3-导入图片信息" class="headerlink" title="3.3.导入图片信息"></a>3.3.导入图片信息</h2><p>现在商品表中虽然有数据，但是所有的图片信息都是无法访问的，我们需要把图片导入到虚拟机：</p>
<p>首先，把课前资料提供的数据上传到虚拟机下：<code>/leyou/static</code>目录：</p>
<p><img src="assets/1527823423289.png" alt="1527823423289"></p>
<p>然后，使用命令解压缩：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">unzip images.zip</span><br></pre></td></tr></table></figure>



<p>修改Nginx配置，使nginx反向代理这些图片地址：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /opt/nginx/config/nginx.conf</span><br></pre></td></tr></table></figure>

<p>修改成如下配置：</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span>       <span class="number">80</span>;</span><br><span class="line">    <span class="attribute">server_name</span>  image.leyou.com;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 监听域名中带有group的，交给FastDFS模块处理</span></span><br><span class="line">    <span class="attribute">location</span> ~/group([<span class="number">0</span>-<span class="number">9</span>])/ &#123;</span><br><span class="line">        ngx_fastdfs_module;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment"># 将其它图片代理指向本地的/leyou/static目录</span></span><br><span class="line">    <span class="attribute">location</span> / &#123;</span><br><span class="line">        <span class="attribute">root</span>   /leyou/static/;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="attribute">error_page</span>   <span class="number">500</span> <span class="number">502</span> <span class="number">503</span> <span class="number">504</span>  /50x.html;</span><br><span class="line">    <span class="attribute">location</span> = /50x.html &#123;</span><br><span class="line">        <span class="attribute">root</span>   html;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="4-商品查询"><a href="#4-商品查询" class="headerlink" title="4.商品查询"></a>4.商品查询</h1><h2 id="4-1-效果预览"><a href="#4-1-效果预览" class="headerlink" title="4.1.效果预览"></a>4.1.效果预览</h2><p>接下来，我们实现商品管理的页面，先看下我们要实现的效果：</p>
<p><img src="assets/1526268595873.png" alt="1526268595873"></p>
<p>可以看出整体是一个table，然后有新增按钮。是不是跟昨天写品牌管理很像？</p>
<p>模板代码在分别在Goods.vue</p>
<p> <img src="assets/1526269215532.png" alt="1526269215532"></p>
<h2 id="4-2-从0开始"><a href="#4-2-从0开始" class="headerlink" title="4.2.从0开始"></a>4.2.从0开始</h2><p>接下来，我们自己来实现一下，新建两个组件：MyGoods.vue和MyGoodsForm.vue</p>
<p> <img src="assets/1526269362233.png" alt="1526269362233"></p>
<p>内容先随意：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;v-card&gt;</span><br><span class="line">      MyGoods</span><br><span class="line">  &lt;&#x2F;v-card&gt;</span><br><span class="line">&lt;&#x2F;template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line"></span><br><span class="line">  export default &#123;</span><br><span class="line">    name: &quot;my-goods&quot;,</span><br><span class="line">    data() &#123;</span><br><span class="line">      return &#123;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&lt;&#x2F;script&gt;</span><br><span class="line"></span><br><span class="line">&lt;style scoped&gt;</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;style&gt;</span><br></pre></td></tr></table></figure>

<p>然后修改menu.js,新建一个菜单：</p>
<p> <img src="assets/1526269555118.png" alt="1526269555118"></p>
<p>修改router/index.js，添加一个路由：</p>
<p> <img src="assets/1526269615639.png" alt="1526269615639"></p>
<p>预览一下：</p>
<p> <img src="assets/1526269739468.png" alt="1526269739468"></p>
<h2 id="4-3-页面实现"><a href="#4-3-页面实现" class="headerlink" title="4.3.页面实现"></a>4.3.页面实现</h2><h3 id="4-3-1-页面基本表格"><a href="#4-3-1-页面基本表格" class="headerlink" title="4.3.1.页面基本表格"></a>4.3.1.页面基本表格</h3><p>商品列表页与品牌列表页几乎一样，我们可以直接去复制一份过来，然后进行一些修改。</p>
<p>首先，字段不一样，商品列表也展示的SPU信息，包含以下字段：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">id：</span><br><span class="line">title：标题</span><br><span class="line">cname：商品分类名称</span><br><span class="line">bname：品牌名称</span><br></pre></td></tr></table></figure>

<p>完整代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">v-card</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;addGoods&quot;</span>&gt;</span>新增商品<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--搜索框，与search属性关联--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-spacer</span>/&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;输入关键字搜索&quot;</span> <span class="attr">v-model.lazy</span>=<span class="string">&quot;search&quot;</span> <span class="attr">append-icon</span>=<span class="string">&quot;search&quot;</span> <span class="attr">hide-details</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-divider</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-data-table</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:headers</span>=<span class="string">&quot;headers&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:items</span>=<span class="string">&quot;goodsList&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:search</span>=<span class="string">&quot;search&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:pagination.sync</span>=<span class="string">&quot;pagination&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:total-items</span>=<span class="string">&quot;totalGoods&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:loading</span>=<span class="string">&quot;loading&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">class</span>=<span class="string">&quot;elevation-1&quot;</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">&quot;items&quot;</span> <span class="attr">slot-scope</span>=<span class="string">&quot;props&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; props.item.id &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span>&gt;</span>&#123;&#123; props.item.title &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span>&gt;</span>&#123;&#123;props.item.cname&#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span>&gt;</span>&#123;&#123; props.item.bname &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;justify-center layout&quot;</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;info&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;editGoods(props.item)&quot;</span>&gt;</span>编辑<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;warning&quot;</span>&gt;</span>删除<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-btn</span> &gt;</span>下架<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-data-table</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--弹出的对话框--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-dialog</span> <span class="attr">max-width</span>=<span class="string">&quot;500&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;show&quot;</span> <span class="attr">persistent</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-card</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--对话框的标题--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-toolbar</span> <span class="attr">dense</span> <span class="attr">dark</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-toolbar-title</span>&gt;</span>&#123;&#123;isEdit ? &#x27;修改&#x27; : &#x27;新增&#x27;&#125;&#125;商品<span class="tag">&lt;/<span class="name">v-toolbar-title</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-spacer</span>/&gt;</span></span><br><span class="line">          <span class="comment">&lt;!--关闭窗口的按钮--&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">icon</span> @<span class="attr">click</span>=<span class="string">&quot;closeWindow&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">v-icon</span>&gt;</span>close<span class="tag">&lt;/<span class="name">v-icon</span>&gt;</span><span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-toolbar</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--对话框的内容，表单--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">my-goods-form</span> <span class="attr">:oldGoods</span>=<span class="string">&quot;oldGoods&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-dialog</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="comment">// 导入自定义的表单组件</span></span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> MyGoodsForm <span class="keyword">from</span> <span class="string">&#x27;./MyGoodsForm&#x27;</span></span></span><br><span class="line"></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="javascript">    name: <span class="string">&quot;my-goods&quot;</span>,</span></span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="javascript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="javascript">        search: <span class="string">&#x27;&#x27;</span>, <span class="comment">// 搜索过滤字段</span></span></span><br><span class="line"><span class="javascript">        totalGoods: <span class="number">0</span>, <span class="comment">// 总条数</span></span></span><br><span class="line"><span class="javascript">        goodsList: [], <span class="comment">// 当前页品牌数据</span></span></span><br><span class="line"><span class="javascript">        loading: <span class="literal">true</span>, <span class="comment">// 是否在加载中</span></span></span><br><span class="line"><span class="javascript">        pagination: &#123;&#125;, <span class="comment">// 分页信息</span></span></span><br><span class="line">        headers: [</span><br><span class="line"><span class="javascript">          &#123;<span class="attr">text</span>: <span class="string">&#x27;id&#x27;</span>, <span class="attr">align</span>: <span class="string">&#x27;center&#x27;</span>, <span class="attr">value</span>: <span class="string">&#x27;id&#x27;</span>&#125;,</span></span><br><span class="line"><span class="javascript">          &#123;<span class="attr">text</span>: <span class="string">&#x27;标题&#x27;</span>, <span class="attr">align</span>: <span class="string">&#x27;center&#x27;</span>, <span class="attr">sortable</span>: <span class="literal">false</span>, <span class="attr">value</span>: <span class="string">&#x27;title&#x27;</span>&#125;,</span></span><br><span class="line"><span class="javascript">          &#123;<span class="attr">text</span>: <span class="string">&#x27;商品分类&#x27;</span>, <span class="attr">align</span>: <span class="string">&#x27;center&#x27;</span>, <span class="attr">sortable</span>: <span class="literal">false</span>, <span class="attr">value</span>: <span class="string">&#x27;cname&#x27;</span>&#125;,</span></span><br><span class="line"><span class="javascript">          &#123;<span class="attr">text</span>: <span class="string">&#x27;品牌&#x27;</span>, <span class="attr">align</span>: <span class="string">&#x27;center&#x27;</span>, <span class="attr">value</span>: <span class="string">&#x27;bname&#x27;</span>, <span class="attr">sortable</span>: <span class="literal">false</span>,&#125;,</span></span><br><span class="line"><span class="javascript">          &#123;<span class="attr">text</span>: <span class="string">&#x27;操作&#x27;</span>, <span class="attr">align</span>: <span class="string">&#x27;center&#x27;</span>, <span class="attr">sortable</span>: <span class="literal">false</span>&#125;</span></span><br><span class="line">        ],</span><br><span class="line"><span class="javascript">        show: <span class="literal">false</span>,<span class="comment">// 控制对话框的显示</span></span></span><br><span class="line"><span class="javascript">        oldGoods: &#123;&#125;, <span class="comment">// 即将被编辑的商品信息</span></span></span><br><span class="line"><span class="javascript">        isEdit: <span class="literal">false</span>, <span class="comment">// 是否是编辑</span></span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line"><span class="javascript">    mounted() &#123; <span class="comment">// 渲染后执行</span></span></span><br><span class="line"><span class="javascript">      <span class="comment">// 查询数据</span></span></span><br><span class="line"><span class="javascript">      <span class="built_in">this</span>.getDataFromServer();</span></span><br><span class="line">    &#125;,</span><br><span class="line">    watch: &#123;</span><br><span class="line"><span class="javascript">      pagination: &#123; <span class="comment">// 监视pagination属性的变化</span></span></span><br><span class="line"><span class="javascript">        deep: <span class="literal">true</span>, <span class="comment">// deep为true，会监视pagination的属性及属性中的对象属性变化</span></span></span><br><span class="line">        handler() &#123;</span><br><span class="line"><span class="javascript">          <span class="comment">// 变化后的回调函数，这里我们再次调用getDataFromServer即可</span></span></span><br><span class="line"><span class="javascript">          <span class="built_in">this</span>.getDataFromServer();</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line"><span class="javascript">      search: &#123; <span class="comment">// 监视搜索字段</span></span></span><br><span class="line">        handler() &#123;</span><br><span class="line"><span class="javascript">          <span class="built_in">this</span>.getDataFromServer();</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line"><span class="javascript">      getDataFromServer() &#123; <span class="comment">// 从服务的加载数的方法。</span></span></span><br><span class="line"><span class="javascript">        <span class="comment">// 发起请求</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.$http.get(<span class="string">&quot;/item/spu/page&quot;</span>, &#123;</span></span><br><span class="line">          params: &#123;</span><br><span class="line"><span class="javascript">            key: <span class="built_in">this</span>.search, <span class="comment">// 搜索条件</span></span></span><br><span class="line"><span class="javascript">            page: <span class="built_in">this</span>.pagination.page,<span class="comment">// 当前页</span></span></span><br><span class="line"><span class="javascript">            rows: <span class="built_in">this</span>.pagination.rowsPerPage,<span class="comment">// 每页大小</span></span></span><br><span class="line"><span class="javascript">            sortBy: <span class="built_in">this</span>.pagination.sortBy,<span class="comment">// 排序字段</span></span></span><br><span class="line"><span class="javascript">            desc: <span class="built_in">this</span>.pagination.descending<span class="comment">// 是否降序</span></span></span><br><span class="line">          &#125;</span><br><span class="line"><span class="javascript">        &#125;).then(<span class="function"><span class="params">resp</span> =&gt;</span> &#123; <span class="comment">// 这里使用箭头函数</span></span></span><br><span class="line"><span class="javascript">          <span class="built_in">this</span>.goodsList = resp.data.items;</span></span><br><span class="line"><span class="javascript">          <span class="built_in">this</span>.totalGoods = resp.data.total;</span></span><br><span class="line"><span class="javascript">          <span class="comment">// 完成赋值后，把加载状态赋值为false</span></span></span><br><span class="line"><span class="javascript">          <span class="built_in">this</span>.loading = <span class="literal">false</span>;</span></span><br><span class="line">        &#125;)</span><br><span class="line">      &#125;,</span><br><span class="line">      addGoods() &#123;</span><br><span class="line"><span class="javascript">        <span class="comment">// 修改标记</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.isEdit = <span class="literal">false</span>;</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 控制弹窗可见：</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.show = <span class="literal">true</span>;</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 把oldBrand变为null</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.oldBrand = <span class="literal">null</span>;</span></span><br><span class="line">      &#125;,</span><br><span class="line">      editGoods(oldGoods)&#123;</span><br><span class="line"><span class="javascript">        <span class="comment">// 修改标记</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.isEdit = <span class="literal">true</span>;</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 控制弹窗可见：</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.show = <span class="literal">true</span>;</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 获取要编辑的brand</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.oldGoods = oldGoods;</span></span><br><span class="line">      &#125;,</span><br><span class="line">      closeWindow()&#123;</span><br><span class="line"><span class="javascript">        <span class="comment">// 重新加载数据</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.getDataFromServer();</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 关闭窗口</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">this</span>.show = <span class="literal">false</span>;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    components:&#123;</span><br><span class="line">      MyGoodsForm</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>主要的改动点：</p>
<ul>
<li><p>页面的<code>v-data-table</code>中的属性绑定修改。items指向goodsList，totalItems指向totalGoods</p>
</li>
<li><p>页面渲染的字段名修改：字段改成商品的SPU字段：id、title，cname(商品分类名称),bname（品牌名称）</p>
</li>
<li><p>data属性修改了以下属性：</p>
<ul>
<li>goodsList：当前页商品数据</li>
<li>totalGoods：商品总数</li>
<li>headers：头信息，需要修改头显示名称</li>
<li>oldGoods：准备要修改的商品</li>
</ul>
</li>
<li><p>加载数据的函数：getDataFromServer，请求的路径进行了修改，另外去除了跟排序相关的查询。SPU查询不排序</p>
</li>
<li><p>新增商品的事件函数：清除了一些数据查询接口，只保留弹窗</p>
</li>
</ul>
<p>查看效果：</p>
<p><img src="assets/1526272476614.png" alt="1526272476614"></p>
<p>因为没有编写查询功能，表格一直处于loading状态。</p>
<p>接下来看弹窗：</p>
<p><img src="assets/1526272537552.png" alt="1526272537552"></p>
<h3 id="4-3-2-上下架状态按钮"><a href="#4-3-2-上下架状态按钮" class="headerlink" title="4.3.2.上下架状态按钮"></a>4.3.2.上下架状态按钮</h3><p>另外，似乎页面少了对上下架商品的过滤，在原始效果图中是有的：</p>
<p> <img src="assets/1526277614649.png" alt="1526277614649"></p>
<p>这在Vuetify中是一组按钮，我们查看帮助文档：</p>
<p> <img src="assets/1526277713391.png" alt="1526277713391"></p>
<p>查看实例得到以下信息：</p>
<p><code>v-btn</code>：一个按钮</p>
<p><code>v-btn-toggle</code>：按钮组，内部可以有多个按钮，点击切换，有以下属性：</p>
<ul>
<li>multiple：是否支持多选，默认是false</li>
<li>value：选中的按钮的值，如果是多选，结果是一个数组；单选，结果是点击的v-btn中的value值，因此按钮组的每个btn都需要指定value属性</li>
</ul>
<p>改造页面：</p>
<p>首先在data中定义一个属性，记录按钮的值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">filter:&#123;</span><br><span class="line">    saleable: <span class="literal">false</span>, <span class="comment">// 上架还是下架</span></span><br><span class="line">    search: <span class="string">&#x27;&#x27;</span>, <span class="comment">// 搜索过滤字段</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里我们的做法是定义一个filter属性，内部在定义search来关联过滤字段，saleable来关联上下架情况。</p>
<p>这样watch就必须监听filter，而不是只监听search了：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">filter: &#123;<span class="comment">// 监视搜索字段</span></span><br><span class="line">  handler() &#123;</span><br><span class="line">    <span class="built_in">this</span>.getDataFromServer();</span><br><span class="line">  &#125;,</span><br><span class="line">  deep:<span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>另外，页面中与search有关的所有字段都需要修改成filter.search:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--搜索框，与search属性关联--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;输入关键字搜索&quot;</span> <span class="attr">v-model.lazy</span>=<span class="string">&quot;filter.search&quot;</span> <span class="attr">append-icon</span>=<span class="string">&quot;search&quot;</span> <span class="attr">hide-details</span>/&gt;</span></span><br></pre></td></tr></table></figure>



<p>然后，在页面中添加按钮组：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">xs3</span>&gt;</span></span><br><span class="line">     状态：</span><br><span class="line">     <span class="tag">&lt;<span class="name">v-btn-toggle</span> <span class="attr">v-model</span>=<span class="string">&quot;filter.saleable&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">flat</span>&gt;</span></span><br><span class="line">             全部</span><br><span class="line">         <span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">flat</span> <span class="attr">:value</span>=<span class="string">&quot;true&quot;</span>&gt;</span></span><br><span class="line">             上架</span><br><span class="line">         <span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">flat</span> <span class="attr">:value</span>=<span class="string">&quot;false&quot;</span>&gt;</span></span><br><span class="line">             下架</span><br><span class="line">         <span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">v-btn-toggle</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>最后，不要忘了在查询时，将saleable携带上：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">getDataFromServer() &#123; <span class="comment">// 从服务的加载数的方法。</span></span><br><span class="line">    <span class="comment">// 发起请求</span></span><br><span class="line">    <span class="built_in">this</span>.$http.get(<span class="string">&quot;/item/spu/page&quot;</span>, &#123;</span><br><span class="line">        params: &#123;</span><br><span class="line">            key: <span class="built_in">this</span>.filter.search, <span class="comment">// 搜索条件</span></span><br><span class="line">            saleable: <span class="built_in">this</span>.filter.saleable, <span class="comment">// 上下架</span></span><br><span class="line">            page: <span class="built_in">this</span>.pagination.page,<span class="comment">// 当前页</span></span><br><span class="line">            rows: <span class="built_in">this</span>.pagination.rowsPerPage,<span class="comment">// 每页大小</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).then(<span class="function"><span class="params">resp</span> =&gt;</span> &#123; <span class="comment">// 这里使用箭头函数</span></span><br><span class="line">        <span class="built_in">this</span>.goodsList = resp.data.items;</span><br><span class="line">        <span class="built_in">this</span>.totalGoods = resp.data.total;</span><br><span class="line">        <span class="comment">// 完成赋值后，把加载状态赋值为false</span></span><br><span class="line">        <span class="built_in">this</span>.loading = <span class="literal">false</span>;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="4-4-后台提供接口"><a href="#4-4-后台提供接口" class="headerlink" title="4.4.后台提供接口"></a>4.4.后台提供接口</h2><p>页面已经准备好，接下来在后台提供分页查询SPU的功能：</p>
<h3 id="4-4-1-实体类"><a href="#4-4-1-实体类" class="headerlink" title="4.4.1.实体类"></a>4.4.1.实体类</h3><blockquote>
<p>SPU</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_spu&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Spu</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> Long brandId;</span><br><span class="line">    <span class="keyword">private</span> Long cid1;<span class="comment">// 1级类目</span></span><br><span class="line">    <span class="keyword">private</span> Long cid2;<span class="comment">// 2级类目</span></span><br><span class="line">    <span class="keyword">private</span> Long cid3;<span class="comment">// 3级类目</span></span><br><span class="line">    <span class="keyword">private</span> String title;<span class="comment">// 标题</span></span><br><span class="line">    <span class="keyword">private</span> String subTitle;<span class="comment">// 子标题</span></span><br><span class="line">    <span class="keyword">private</span> Boolean saleable;<span class="comment">// 是否上架</span></span><br><span class="line">    <span class="keyword">private</span> Boolean valid;<span class="comment">// 是否有效，逻辑删除用</span></span><br><span class="line">    <span class="keyword">private</span> Date createTime;<span class="comment">// 创建时间</span></span><br><span class="line">    <span class="keyword">private</span> Date lastUpdateTime;<span class="comment">// 最后修改时间</span></span><br><span class="line">    <span class="comment">// 省略getter和setter</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>SPU详情</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name=&quot;tb_spu_detail&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpuDetail</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="keyword">private</span> Long spuId;<span class="comment">// 对应的SPU的id</span></span><br><span class="line">    <span class="keyword">private</span> String description;<span class="comment">// 商品描述</span></span><br><span class="line">    <span class="keyword">private</span> String specTemplate;<span class="comment">// 商品特殊规格的名称及可选值模板</span></span><br><span class="line">    <span class="keyword">private</span> String specifications;<span class="comment">// 商品的全局规格属性</span></span><br><span class="line">    <span class="keyword">private</span> String packingList;<span class="comment">// 包装清单</span></span><br><span class="line">    <span class="keyword">private</span> String afterService;<span class="comment">// 售后服务</span></span><br><span class="line">    <span class="comment">// 省略getter和setter</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="4-4-2-controller"><a href="#4-4-2-controller" class="headerlink" title="4.4.2.controller"></a>4.4.2.controller</h3><p>先分析：</p>
<ul>
<li><p>请求方式：GET</p>
</li>
<li><p>请求路径：/spu/page</p>
</li>
<li><p>请求参数：</p>
<ul>
<li>page：当前页</li>
<li>rows：每页大小</li>
<li>key：过滤条件</li>
<li>saleable：上架或下架</li>
</ul>
</li>
<li><p>返回结果：商品SPU的分页信息。</p>
<ul>
<li><p>要注意，页面展示的是商品分类和品牌名称，而数据库中保存的是id，怎么办？</p>
<p>我们可以新建一个类，继承SPU，并且拓展cname和bname属性，写到<code>ly-item-interface</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpuBo</span> <span class="keyword">extends</span> <span class="title">Spu</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    String cname;<span class="comment">// 商品分类名称</span></span><br><span class="line">    </span><br><span class="line">    String bname;<span class="comment">// 品牌名称</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 略 。。</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
</li>
</ul>
<p>编写controller代码：</p>
<p>我们把与商品相关的一切业务接口都放到一起，起名为GoodsController，业务层也是这样</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GoodsController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> GoodsService goodsService;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查询SPU</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> page</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> rows</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/spu/page&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> ResponseEntity&lt;PageResult&lt;SpuBo&gt;&gt; querySpuByPage(</span><br><span class="line">            <span class="meta">@RequestParam(value = &quot;page&quot;, defaultValue = &quot;1&quot;)</span> Integer page,</span><br><span class="line">            <span class="meta">@RequestParam(value = &quot;rows&quot;, defaultValue = &quot;5&quot;)</span> Integer rows,</span><br><span class="line">            <span class="meta">@RequestParam(value = &quot;key&quot;, required = false)</span> String key) &#123;</span><br><span class="line">        <span class="comment">// 分页查询spu信息</span></span><br><span class="line">        PageResult&lt;SpuBo&gt; result = <span class="keyword">this</span>.goodsService.querySpuByPageAndSort(page, rows, key);</span><br><span class="line">        <span class="keyword">if</span> (result == <span class="keyword">null</span> || result.getItems().size() == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ResponseEntity.ok(result);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="4-4-3-service"><a href="#4-4-3-service" class="headerlink" title="4.4.3.service"></a>4.4.3.service</h3><p>所有商品相关的业务（包括SPU和SKU）放到一个业务下：GoodsService。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GoodsService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SpuMapper spuMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CategoryService categoryService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BrandMapper brandMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> PageResult&lt;SpuBo&gt; <span class="title">querySpuByPageAndSort</span><span class="params">(Integer page, Integer rows, Boolean saleable, String key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 1、查询SPU</span></span><br><span class="line">        <span class="comment">// 分页,最多允许查100条</span></span><br><span class="line">        PageHelper.startPage(page, Math.min(rows, <span class="number">100</span>));</span><br><span class="line">        <span class="comment">// 创建查询条件</span></span><br><span class="line">        Example example = <span class="keyword">new</span> Example(Spu.class);</span><br><span class="line">        Example.Criteria criteria = example.createCriteria();</span><br><span class="line">        <span class="comment">// 是否过滤上下架</span></span><br><span class="line">        <span class="keyword">if</span> (saleable != <span class="keyword">null</span>) &#123;</span><br><span class="line">            criteria.orEqualTo(<span class="string">&quot;saleable&quot;</span>, saleable);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 是否模糊查询</span></span><br><span class="line">        <span class="keyword">if</span> (StringUtils.isNotBlank(key)) &#123;</span><br><span class="line">            criteria.andLike(<span class="string">&quot;title&quot;</span>, <span class="string">&quot;%&quot;</span> + key + <span class="string">&quot;%&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        Page&lt;Spu&gt; pageInfo = (Page&lt;Spu&gt;) <span class="keyword">this</span>.spuMapper.selectByExample(example);</span><br><span class="line"></span><br><span class="line">        List&lt;SpuBo&gt; list = pageInfo.getResult().stream().map(spu -&gt; &#123;</span><br><span class="line">            <span class="comment">// 2、把spu变为 spuBo</span></span><br><span class="line">            SpuBo spuBo = <span class="keyword">new</span> SpuBo();</span><br><span class="line">            <span class="comment">// 属性拷贝</span></span><br><span class="line">            BeanUtils.copyProperties(spu, spuBo);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 3、查询spu的商品分类名称,要查三级分类</span></span><br><span class="line">            List&lt;String&gt; names = <span class="keyword">this</span>.categoryService.queryNameByIds(</span><br><span class="line">                    Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));</span><br><span class="line">            <span class="comment">// 将分类名称拼接后存入</span></span><br><span class="line">            spuBo.setCname(StringUtils.join(names, <span class="string">&quot;/&quot;</span>));</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 4、查询spu的品牌名称</span></span><br><span class="line">            Brand brand = <span class="keyword">this</span>.brandMapper.selectByPrimaryKey(spu.getBrandId());</span><br><span class="line">            spuBo.setBname(brand.getName());</span><br><span class="line">            <span class="keyword">return</span> spuBo;</span><br><span class="line">        &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> PageResult&lt;&gt;(pageInfo.getTotal(), list);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="4-4-4-mapper"><a href="#4-4-4-mapper" class="headerlink" title="4.4.4.mapper"></a>4.4.4.mapper</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SpuMapper</span> <span class="keyword">extends</span> <span class="title">Mapper</span>&lt;<span class="title">Spu</span>&gt; </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="4-4-5-Category中拓展查询名称的功能"><a href="#4-4-5-Category中拓展查询名称的功能" class="headerlink" title="4.4.5.Category中拓展查询名称的功能"></a>4.4.5.Category中拓展查询名称的功能</h3><p>页面需要商品的分类名称需要在这里查询，因此要额外提供查询分类名称的功能，</p>
<p>在CategoryService中添加功能：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> List&lt;String&gt; <span class="title">queryNameByIds</span><span class="params">(List&lt;Long&gt; ids)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.categoryMapper.selectByIdList(ids).stream().map(Category::getName).collect(Collectors.toList());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>mapper的selectByIDList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CategoryMapper</span> <span class="keyword">extends</span> <span class="title">Mapper</span>&lt;<span class="title">Category</span>&gt;, <span class="title">SelectByIdListMapper</span>&lt;<span class="title">Category</span>, <span class="title">Long</span>&gt; </span>&#123; </span><br><span class="line">    <span class="comment">// ...coding</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="4-5-测试"><a href="#4-5-测试" class="headerlink" title="4.5.测试"></a>4.5.测试</h2><p>刷新页面，查看效果：</p>
<p> <img src="assets/1526280777975.png" alt="1526280777975"></p>
<p>基本与预览的效果一致，OK！</p>
<h1 id="5-商品新增"><a href="#5-商品新增" class="headerlink" title="5.商品新增"></a>5.商品新增</h1><h2 id="5-1-效果预览"><a href="#5-1-效果预览" class="headerlink" title="5.1.效果预览"></a>5.1.效果预览</h2><p>新增商品窗口：</p>
<p> <img src="../../../%E4%B9%90%E4%BC%98%E5%95%86%E5%9F%8E/%E7%AC%94%E8%AE%B0/day08/assets/1526268739827.png" alt="1526268739827"></p>
<p>这个表单比较复杂，因为商品的信息比较多，分成了4个部分来填写：</p>
<ul>
<li>基本信息</li>
<li>商品描述信息</li>
<li>规格参数信息</li>
<li>SKU信息</li>
</ul>
<h2 id="5-2-从0开始"><a href="#5-2-从0开始" class="headerlink" title="5.2.从0开始"></a>5.2.从0开始</h2><p>我们刚刚在查询时，已经实现创建了MyGoodsForm.vue，并且已经在MyGoods中引入。</p>
<p>不过目前没有写代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;v-card&gt;</span><br><span class="line">    my goods form</span><br><span class="line">  &lt;&#x2F;v-card&gt;</span><br><span class="line">&lt;&#x2F;template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">  export default &#123;</span><br><span class="line">    name: &quot;my-goods-form&quot;,</span><br><span class="line">    props: &#123;</span><br><span class="line">      oldGoods: &#123;</span><br><span class="line">        type: Object</span><br><span class="line">      &#125;,</span><br><span class="line">      isEdit: &#123;</span><br><span class="line">        type: Boolean,</span><br><span class="line">        default: false</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    data() &#123;</span><br><span class="line">      return &#123;</span><br><span class="line"></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&lt;&#x2F;script&gt;</span><br><span class="line"></span><br><span class="line">&lt;style scoped&gt;</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;style&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后在MyBrand中，已经引入了MyGoodsForm组件，并且页面中也形成了对话框：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 导入自定义的表单组件</span></span><br><span class="line"><span class="keyword">import</span> MyGoodsForm <span class="keyword">from</span> <span class="string">&#x27;./MyGoodsForm&#x27;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-dialog</span> <span class="attr">max-width</span>=<span class="string">&quot;500&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;show&quot;</span> <span class="attr">persistent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--对话框的标题--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-toolbar</span> <span class="attr">dense</span> <span class="attr">dark</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-toolbar-title</span>&gt;</span>&#123;&#123;isEdit ? &#x27;修改&#x27; : &#x27;新增&#x27;&#125;&#125;商品<span class="tag">&lt;/<span class="name">v-toolbar-title</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-spacer</span>/&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--关闭窗口的按钮--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">icon</span> @<span class="attr">click</span>=<span class="string">&quot;closeWindow&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-icon</span>&gt;</span>close<span class="tag">&lt;/<span class="name">v-icon</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-toolbar</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--对话框的内容，表单--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">my-goods-form</span> <span class="attr">:oldGoods</span>=<span class="string">&quot;oldGoods&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-dialog</span>&gt;</span></span><br></pre></td></tr></table></figure>



<p>并且也已经给新增按钮绑定了点击事件：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;addGoods&quot;</span>&gt;</span>新增商品<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>addGoods方法中，设置对话框的show属性为true：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">addGoods() &#123;</span><br><span class="line">    <span class="comment">// 修改标记</span></span><br><span class="line">    <span class="built_in">this</span>.isEdit = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">// 控制弹窗可见：</span></span><br><span class="line">    <span class="built_in">this</span>.show = <span class="literal">true</span>;</span><br><span class="line">    <span class="comment">// 把oldBrand变为null</span></span><br><span class="line">    <span class="built_in">this</span>.oldBrand = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>不过弹窗中没有任何数据：</p>
<p> <img src="assets/1526281134046.png" alt="1526281134046"></p>
<h2 id="5-3-新增商品页的基本框架"><a href="#5-3-新增商品页的基本框架" class="headerlink" title="5.3.新增商品页的基本框架"></a>5.3.新增商品页的基本框架</h2><h3 id="5-3-1-Steppers，步骤线"><a href="#5-3-1-Steppers，步骤线" class="headerlink" title="5.3.1.Steppers，步骤线"></a>5.3.1.Steppers，步骤线</h3><p>预览效果图中，分四个步骤显示商品表单的组件，叫做stepper，看下文档：</p>
<p><img src="assets/1526281659782.png" alt="1526281659782"></p>
<p>其基本结构如图：</p>
<p><img src="assets/1526282309386.png" alt="1526282309386"></p>
<p>一个步骤线（v-stepper）总的分为两部分：</p>
<ul>
<li>v-stepper-header：代表步骤的头部进度条，只能有一个<ul>
<li>v-stepper-step：代表进度条的每一个步骤，可以有多个</li>
</ul>
</li>
<li>v-stepper-items：代表当前步骤下的内容组，只能有一个，内部有stepper-content<ul>
<li>v-stepper-content：代表每一步骤的页面内容，可以有多个</li>
</ul>
</li>
</ul>
<blockquote>
<p>v-stepper</p>
</blockquote>
<ul>
<li>value：其值是当前所在的步骤索引，可以用来控制步骤切换</li>
<li>dark：是否使用黑暗色调，默认false</li>
<li>non-linear：是否启用非线性步骤，用户不用按顺序切换，而是可以调到任意步骤，默认false</li>
<li>vertical：是否垂直显示步骤线，默认是false，即水平显示</li>
</ul>
<blockquote>
<p>v-stepper-header的属性：</p>
</blockquote>
<ul>
<li>无</li>
</ul>
<blockquote>
<p>v-stepper-step的属性</p>
</blockquote>
<ul>
<li>color：颜色</li>
<li>complete：当前步骤是否已经完成，布尔值</li>
<li>editable：是否可编辑任意步骤（非线性步骤）</li>
<li>step：步骤索引</li>
</ul>
<blockquote>
<p>v-stepper-items</p>
</blockquote>
<ul>
<li>无</li>
</ul>
<blockquote>
<p>v-stepper-content</p>
</blockquote>
<ul>
<li>step：步骤索引，需要与v-stepper-step中的对应</li>
</ul>
<h3 id="5-3-2-编写页面"><a href="#5-3-2-编写页面" class="headerlink" title="5.3.2.编写页面"></a>5.3.2.编写页面</h3><p>首先我们在data中定义一个变量，记录当前的步骤数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">data() &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        step: <span class="number">1</span>, <span class="comment">// 当前的步骤数，默认为1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>

<p>然后在模板页面中引入步骤线：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-stepper</span> <span class="attr">v-model</span>=<span class="string">&quot;step&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-stepper-header</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-step</span> <span class="attr">:complete</span>=<span class="string">&quot;step &gt; 1&quot;</span> <span class="attr">step</span>=<span class="string">&quot;1&quot;</span>&gt;</span>基本信息<span class="tag">&lt;/<span class="name">v-stepper-step</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-divider</span>/&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-step</span> <span class="attr">:complete</span>=<span class="string">&quot;step &gt; 2&quot;</span> <span class="attr">step</span>=<span class="string">&quot;2&quot;</span>&gt;</span>商品描述<span class="tag">&lt;/<span class="name">v-stepper-step</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-divider</span>/&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-step</span> <span class="attr">:complete</span>=<span class="string">&quot;step &gt; 3&quot;</span> <span class="attr">step</span>=<span class="string">&quot;3&quot;</span>&gt;</span>规格参数<span class="tag">&lt;/<span class="name">v-stepper-step</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-divider</span>/&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-step</span> <span class="attr">step</span>=<span class="string">&quot;4&quot;</span>&gt;</span>SKU属性<span class="tag">&lt;/<span class="name">v-stepper-step</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-stepper-header</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-stepper-items</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;1&quot;</span>&gt;</span></span><br><span class="line">        基本信息</span><br><span class="line">      <span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;2&quot;</span>&gt;</span></span><br><span class="line">        商品描述</span><br><span class="line">      <span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;3&quot;</span>&gt;</span></span><br><span class="line">        规格参数</span><br><span class="line">      <span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;4&quot;</span>&gt;</span></span><br><span class="line">        SKU属性</span><br><span class="line">      <span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-stepper-items</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">v-stepper</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>效果：</p>
<p><img src="assets/1526283729193.png" alt="1526283729193"></p>
<p>步骤线出现了！</p>
<p>那么问题来了：该如何让这几个步骤切换呢？</p>
<h3 id="5-3-3-步骤切换按钮"><a href="#5-3-3-步骤切换按钮" class="headerlink" title="5.3.3.步骤切换按钮"></a>5.3.3.步骤切换按钮</h3><h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><p>如果改变step的值与指定的步骤索引一致，就可以实现步骤切换了：</p>
<p><img src="assets/1526283971416.png" alt="1526283971416"></p>
<p>因此，我们需要定义两个按钮，点击后修改step的值，让步骤前进或后退。</p>
<p>那么这两个按钮放哪里？</p>
<p>如果放在MyGoodsForm内，当表单内容过多时，按钮会被挤压到屏幕最下方，不够友好。最好是能够悬停状态。</p>
<p>所以，按钮必须放到MyGoods组件中，也就是父组件。</p>
<p>父组件的对话框是一个card，card组件提供了一个滚动效果，scrollable，如果为true，card的内容滚动时，其头部和底部是可以静止的。</p>
<p>现在card的头部是弹框的标题，card的中间就是表单内容。如果我们把按钮放到底部，就可以实现悬停效果。</p>
<h4 id="页面添加按钮"><a href="#页面添加按钮" class="headerlink" title="页面添加按钮"></a>页面添加按钮</h4><p>改造MyGoods的对话框组件：</p>
<p>  <img src="assets/1526285123845.png" alt="1526285123845"></p>
<p>查看页面：</p>
<p><img src="assets/1526285086042.png" alt="1526285086042"></p>
<h4 id="添加点击事件"><a href="#添加点击事件" class="headerlink" title="添加点击事件"></a>添加点击事件</h4><p>现在这两个按钮点击后没有任何反应。我们需要给他们绑定点击事件，来修改MyGoodsForm中的step的值。</p>
<p>也就是说，父组件要修改子组件的属性状态。想到什么了？</p>
<p>props属性。</p>
<p>我们先在父组件定义一个step属性：</p>
<p> <img src="assets/1526285364638.png" alt="1526285364638"></p>
<p>然后在点击事件中修改它：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">previous()&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.step &gt; <span class="number">1</span>)&#123;</span><br><span class="line">        <span class="built_in">this</span>.step--</span><br><span class="line">    &#125;</span><br><span class="line">&#125;,</span><br><span class="line">next()&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.step &lt; <span class="number">4</span>)&#123;</span><br><span class="line">        <span class="built_in">this</span>.step++</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>页面绑定事件：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--底部按钮，用来操作步骤线--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-card-actions</span> <span class="attr">class</span>=<span class="string">&quot;elevation-10&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">class</span>=<span class="string">&quot;xs3 mx-auto&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-btn</span> @<span class="attr">click</span>=<span class="string">&quot;previous&quot;</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">:disabled</span>=<span class="string">&quot;step === 1&quot;</span>&gt;</span>上一步<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-btn</span> @<span class="attr">click</span>=<span class="string">&quot;next&quot;</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">:disabled</span>=<span class="string">&quot;step === 4&quot;</span>&gt;</span>下一步<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-card-actions</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>然后把step属性传递给子组件：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--对话框的内容，表单--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">class</span>=<span class="string">&quot;px-3&quot;</span> <span class="attr">style</span>=<span class="string">&quot;height: 600px&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">my-goods-form</span> <span class="attr">:oldGoods</span>=<span class="string">&quot;oldGoods&quot;</span> <span class="attr">:step</span>=<span class="string">&quot;step&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>子组件中接收属性：</p>
<p> <img src="assets/1526285994382.png" alt="1526285994382"></p>
<p>测试效果：</p>
<p> <img src="assets/10.gif"></p>
<h2 id="5-4-商品基本信息"><a href="#5-4-商品基本信息" class="headerlink" title="5.4.商品基本信息"></a>5.4.商品基本信息</h2><p>商品基本信息，主要是一些纯文本比较简单的SPU属性，例如：</p>
<p>商品分类、商品品牌、商品标题、商品卖点（子标题），包装清单，售后服务</p>
<p>接下来，我们一一添加这些表单项。</p>
<p>注：这里为了简化，我们就不进行form表单校验了。之前已经讲过。</p>
<h3 id="5-4-1-在data中定义Goods属性"><a href="#5-4-1-在data中定义Goods属性" class="headerlink" title="5.4.1.在data中定义Goods属性"></a>5.4.1.在data中定义Goods属性</h3><p>首先，我们需要定义一个goods对象，包括商品的上述属性。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">data() &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        goods:&#123;</span><br><span class="line">            categories:&#123;&#125;, <span class="comment">// 商品3级分类数组信息</span></span><br><span class="line">            brandId: <span class="number">0</span>,<span class="comment">// 品牌id信息</span></span><br><span class="line">            title: <span class="string">&#x27;&#x27;</span>,<span class="comment">// 标题</span></span><br><span class="line">            subTitle: <span class="string">&#x27;&#x27;</span>,<span class="comment">// 子标题</span></span><br><span class="line">            spuDetail: &#123;</span><br><span class="line">                packingList: <span class="string">&#x27;&#x27;</span>,<span class="comment">// 包装列表</span></span><br><span class="line">                afterService: <span class="string">&#x27;&#x27;</span>,<span class="comment">// 售后服务</span></span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>注意，这里我们在goods中定义了spuDetail属性，然后把包装列表和售后服务作为它的属性，这样符合数据库的结构。</p>
<h3 id="5-4-2-商品分类选框"><a href="#5-4-2-商品分类选框" class="headerlink" title="5.4.2.商品分类选框"></a>5.4.2.商品分类选框</h3><p>商品分类选框之前我们已经做过了。是级联选框。直接拿来用：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-cascader</span></span></span><br><span class="line"><span class="tag">        <span class="attr">url</span>=<span class="string">&quot;/item/category/list&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">required</span></span></span><br><span class="line"><span class="tag">        <span class="attr">showAllLevels</span></span></span><br><span class="line"><span class="tag">        <span class="attr">v-model</span>=<span class="string">&quot;goods.categories&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">label</span>=<span class="string">&quot;请选择商品分类&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>跟以前使用有一些区别：</p>
<ul>
<li>一个商品只能有一个分类，所以这里去掉了multiple属性</li>
<li>商品SPU中要保存3级商品分类，因此我们这里需要选择showAllLevels属性，显示所有3级分类</li>
</ul>
<p>效果：</p>
<p> <img src="assets/1526290196168.png" alt="1526290196168"></p>
<p>查看goods的属性，三级类目都在：</p>
<p> <img src="assets/1526290263252.png" alt="1526290263252"></p>
<h3 id="5-4-3-品牌选择"><a href="#5-4-3-品牌选择" class="headerlink" title="5.4.3.品牌选择"></a>5.4.3.品牌选择</h3><h4 id="select组件"><a href="#select组件" class="headerlink" title="select组件"></a>select组件</h4><p>品牌不分级别，使用普通下拉选框即可。我们查看官方文档的下拉选框说明：</p>
<p> <img src="assets/1526287929674.png" alt="1526287929674"></p>
<p>组件名：v-select</p>
<p>比较重要的一些属性：</p>
<ul>
<li>item-text：选项中用来展示的字段名，默认是text</li>
<li>item-value：选项中用来作为value值的字段名，默认是value</li>
<li>items：待选项的对象数组</li>
<li>label：提示文本</li>
<li>multiple：是否支持多选，默认是false</li>
</ul>
<p>其它次要属性：</p>
<ul>
<li>autocomplete：是否根据用户输入的文本进行搜索过滤（自动），默认false</li>
<li>chips：是否以小纸片方式显示用户选中的项，默认false</li>
<li>clearable：是否添加清空选项图标，默认是false</li>
<li>color：颜色</li>
<li>dense：是否压缩选择框高度，默认false</li>
<li>editable：是否可编辑，默认false</li>
<li>hide-details：是否隐藏错误提示，默认false</li>
<li>hide-selected：是否在菜单中隐藏已选择的项</li>
<li>hint：提示文本</li>
<li>其它基本与<code>v-text-filed</code>组件类似，不再一一列举</li>
</ul>
<h4 id="页面实现"><a href="#页面实现" class="headerlink" title="页面实现"></a>页面实现</h4><p>备选项items需要我们去后台查询，而且必须是在用户选择商品分类后去查询。</p>
<p>我们定义一个属性，保存品牌的待选项信息：</p>
<p> <img src="assets/1526289926110.png" alt="1526289926110"></p>
<p>然后编写一个watch，监控goods.categories的变化：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">watch: &#123;</span><br><span class="line">    <span class="string">&#x27;goods.categories&#x27;</span>: &#123;</span><br><span class="line">        deep: <span class="literal">true</span>,</span><br><span class="line">            handler(val) &#123;</span><br><span class="line">            <span class="comment">// 判断商品分类是否存在，存在才查询</span></span><br><span class="line">            <span class="keyword">if</span> (val &amp;&amp; val.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// 根据分类查询品牌</span></span><br><span class="line">                <span class="built_in">this</span>.$http.get(<span class="string">&quot;/item/brand/cid/&quot;</span> + <span class="built_in">this</span>.goods.categories[<span class="number">2</span>].id)</span><br><span class="line">                    .then(<span class="function">(<span class="params">&#123;data&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">                    <span class="built_in">this</span>.brandOptions = data;</span><br><span class="line">                &#125;)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>我们的品牌对象包含以下字段：id、name、letter、image。显然item-text应该对应name，item-value应该对应id</p>
<p>因此我们添加一个选框，指定item-text和item-value</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--品牌--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-select</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:items</span>=<span class="string">&quot;brandOptions&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">item-text</span>=<span class="string">&quot;name&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">item-value</span>=<span class="string">&quot;id&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">label</span>=<span class="string">&quot;所属品牌&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">&quot;goods.brandId&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">required</span></span></span><br><span class="line"><span class="tag">      <span class="attr">autocomplete</span></span></span><br><span class="line"><span class="tag">      <span class="attr">clearable</span></span></span><br><span class="line"><span class="tag">      <span class="attr">dense</span> <span class="attr">chips</span></span></span><br><span class="line"><span class="tag">      /&gt;</span></span><br></pre></td></tr></table></figure>





<h4 id="后台提供接口"><a href="#后台提供接口" class="headerlink" title="后台提供接口"></a>后台提供接口</h4><p>页面需要去后台查询品牌信息，我们自然需要提供：</p>
<blockquote>
<p>controller</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 根据分类查询品牌</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> cid</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;cid/&#123;cid&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> ResponseEntity&lt;List&lt;Brand&gt;&gt; queryBrandByCategory(<span class="meta">@PathVariable(&quot;cid&quot;)</span> Long cid) &#123;</span><br><span class="line">    List&lt;Brand&gt; list = <span class="keyword">this</span>.brandService.queryBrandByCategory(cid);</span><br><span class="line">    <span class="keyword">if</span>(list == <span class="keyword">null</span>)&#123;</span><br><span class="line">        <span class="keyword">new</span> ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ResponseEntity.ok(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>service</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> List&lt;Brand&gt; <span class="title">queryBrandByCategory</span><span class="params">(Long cid)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.brandMapper.queryByCategoryId(cid);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>mapper</p>
</blockquote>
<p>根据分类查询品牌有中间表，需要自己编写Sql：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Select(&quot;SELECT b.* FROM tb_brand b LEFT JOIN tb_category_brand cb ON b.id = cb.brand_id WHERE cb.category_id = #&#123;cid&#125;&quot;)</span></span><br><span class="line"><span class="function">List&lt;Brand&gt; <span class="title">queryByCategoryId</span><span class="params">(Long cid)</span></span>;</span><br></pre></td></tr></table></figure>



<h4 id="测试效果"><a href="#测试效果" class="headerlink" title="测试效果"></a>测试效果</h4><p> <img src="assets/1526290461916.png" alt="1526290461916"></p>
<h3 id="5-4-4-标题等其它字段"><a href="#5-4-4-标题等其它字段" class="headerlink" title="5.4.4.标题等其它字段"></a>5.4.4.标题等其它字段</h3><p>标题等字段都是普通文本，直接使用<code>v-text-field</code>即可：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;商品标题&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.title&quot;</span> <span class="attr">:counter</span>=<span class="string">&quot;200&quot;</span> <span class="attr">required</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;商品卖点&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.subTitle&quot;</span> <span class="attr">:counter</span>=<span class="string">&quot;200&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;包装清单&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.spuDetail.packingList&quot;</span> <span class="attr">:counter</span>=<span class="string">&quot;1000&quot;</span> <span class="attr">multi-line</span> <span class="attr">:rows</span>=<span class="string">&quot;3&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">label</span>=<span class="string">&quot;售后服务&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.spuDetail.afterService&quot;</span> <span class="attr">:counter</span>=<span class="string">&quot;1000&quot;</span> <span class="attr">multi-line</span> <span class="attr">:rows</span>=<span class="string">&quot;3&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>一些新的属性：</p>
<ul>
<li>counter：计数器，记录当前用户输入的文本字数</li>
<li>rows：文本域的行数</li>
<li>multi-line：把单行文本变成文本域</li>
</ul>
<p><img src="assets/1526290761721.png" alt="1526290761721"></p>
<h2 id="5-5-商品描述信息"><a href="#5-5-商品描述信息" class="headerlink" title="5.5.商品描述信息"></a>5.5.商品描述信息</h2><p>商品描述信息比较复杂，而且图文并茂，甚至包括视频。</p>
<p>这样的内容，一般都会使用富文本编辑器。</p>
<h3 id="5-5-1-什么是富文本编辑器"><a href="#5-5-1-什么是富文本编辑器" class="headerlink" title="5.5.1.什么是富文本编辑器"></a>5.5.1.什么是富文本编辑器</h3><p>百度百科：</p>
<p><img src="assets/1526290914491.png" alt="1526290914491"></p>
<p>通俗来说：富文本，就是比较丰富的文本编辑器。普通的框只能输入文字，而富文本还能给文字加颜色样式等。</p>
<p>富文本编辑器有很多，例如：KindEditor、Ueditor。但并不原生支持vue</p>
<p>但是我们今天要说的，是一款支持Vue的富文本编辑器：<code>vue-quill-editor</code></p>
<h3 id="5-5-2-Vue-Quill-Editor"><a href="#5-5-2-Vue-Quill-Editor" class="headerlink" title="5.5.2.Vue-Quill-Editor"></a>5.5.2.Vue-Quill-Editor</h3><p>GitHub的主页：<a target="_blank" rel="noopener" href="https://github.com/surmon-china/vue-quill-editor">https://github.com/surmon-china/vue-quill-editor</a></p>
<p>Vue-Quill-Editor是一个基于Quill的富文本编辑器：<a target="_blank" rel="noopener" href="https://quilljs.com/">Quill的官网</a></p>
<p><img src="assets/1526291232678.png" alt="1526291232678"></p>
<h3 id="5-5-3-使用指南"><a href="#5-5-3-使用指南" class="headerlink" title="5.5.3.使用指南"></a>5.5.3.使用指南</h3><p>使用非常简单：</p>
<p>第一步：安装，使用npm命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install vue-quill-editor --save</span><br></pre></td></tr></table></figure>

<p>第二步：加载，在js中引入：</p>
<p>全局使用：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line"><span class="keyword">import</span> VueQuillEditor <span class="keyword">from</span> <span class="string">&#x27;vue-quill-editor&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> options = &#123;&#125;; <span class="comment">/* &#123; default global options &#125; */</span></span><br><span class="line"></span><br><span class="line">Vue.use(VueQuillEditor, options); <span class="comment">// options可选</span></span><br></pre></td></tr></table></figure>



<p>局部使用：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;quill/dist/quill.core.css&#x27;</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;quill/dist/quill.snow.css&#x27;</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;quill/dist/quill.bubble.css&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123;quillEditor&#125; <span class="keyword">from</span> <span class="string">&#x27;vue-quill-editor&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> vm = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">    components:&#123;</span><br><span class="line">        quillEditor</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<p>第三步：页面引用：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">quill-editor</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.spuDetail.description&quot;</span> <span class="attr">:options</span>=<span class="string">&quot;editorOption&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>



<h3 id="5-5-4-自定义的富文本编辑器"><a href="#5-5-4-自定义的富文本编辑器" class="headerlink" title="5.5.4.自定义的富文本编辑器"></a>5.5.4.自定义的富文本编辑器</h3><p>不过这个组件有个小问题，就是图片上传的无法直接上传到后台，因此我们对其进行了封装，支持了图片的上传。</p>
<p> <img src="assets/1526296083605.png" alt="1526296083605.png"></p>
<p>使用也非常简单：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;2&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-editor</span> <span class="attr">v-model</span>=<span class="string">&quot;goods.spuDetail.description&quot;</span> <span class="attr">upload-url</span>=<span class="string">&quot;/upload/image&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>upload-url：是图片上传的路径</li>
<li>v-model：双向绑定，将富文本编辑器的内容绑定到goods.spuDetail.description</li>
</ul>
<h3 id="5-5-5-效果："><a href="#5-5-5-效果：" class="headerlink" title="5.5.5.效果："></a>5.5.5.效果：</h3><p><img src="assets/1526297276667.png" alt="1526297276667"></p>
<h2 id="5-6-规格参数"><a href="#5-6-规格参数" class="headerlink" title="5.6.规格参数"></a>5.6.规格参数</h2><p>商品规格参数与商品分类绑定，因此我们需要在用户选择商品分类后，去后台查询对应的规格参数模板。</p>
<h3 id="5-6-1-查询商品规格"><a href="#5-6-1-查询商品规格" class="headerlink" title="5.6.1.查询商品规格"></a>5.6.1.查询商品规格</h3><p>首先，我们在data中定义变量，记录查询到的规格参数模板：</p>
<p> <img src="assets/1526297766476.png" alt="1526297766476"></p>
<p>然后，我们通过watch监控goods.categories的变化，然后去查询规格：</p>
<p>  <img src="assets/1526375545366.png" alt="1526375545366"></p>
<p>查看是否查询到：</p>
<p> <img src="assets/1526297980403.png" alt="1526297980403"></p>
<h3 id="5-6-2-页面展示规格属性"><a href="#5-6-2-页面展示规格属性" class="headerlink" title="5.6.2.页面展示规格属性"></a>5.6.2.页面展示规格属性</h3><p>获取到了规格参数，还需要把它展示到页面中。</p>
<p>现在查询到的规格参数只有key，并没有值。值需要用户来根据SPU信息填写，因此规格参数最终需要处理为表单。</p>
<blockquote>
<p>整体结构</p>
</blockquote>
<p>整体来看，规格参数是数组，每个元素是一组规格的集合。我们需要分组来展示。比如每组放到一个card中。</p>
<blockquote>
<p>注意事项：</p>
</blockquote>
<p>规格参数中的属性有一些需要我们特殊处理：</p>
<p> <img src="assets/1526298277201.png" alt="1526298277201"></p>
<ul>
<li>global：是否是全局属性，规格参数中一部分是SPU共享，属于全局属性，另一部是SKU特有，需要根据SKU来填写。因此，在当前版面中，只展示global为true的，即全局属性。sku特有属性放到最后一个面板</li>
<li>numerical：是否是数值类型，如果是，把单位补充在页面表单，不允许用户填写，并且要验证用户输入的数据格式</li>
<li>options：是否有可选项，如果有，则使用下拉选框来渲染。</li>
</ul>
<p>页面代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--3、规格参数--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;3&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">class</span>=<span class="string">&quot;xs10 mx-auto px-3&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--遍历整个规格参数，获取每一组--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-card</span> <span class="attr">v-for</span>=<span class="string">&quot;spec in specifications&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;spec.group&quot;</span> <span class="attr">class</span>=<span class="string">&quot;my-2&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--组名称--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-card-title</span> <span class="attr">class</span>=<span class="string">&quot;subheading&quot;</span>&gt;</span>&#123;&#123;spec.group&#125;&#125;<span class="tag">&lt;/<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--遍历组中的每个属性，并判断是否是全局属性，不是则不显示--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">v-for</span>=<span class="string">&quot;param in spec.params&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;param.k&quot;</span> <span class="attr">v-if</span>=<span class="string">&quot;param.global&quot;</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">                <span class="comment">&lt;!--判断是否有可选项，如果没有，则显示文本框。还要判断是否是数值类型，如果是把unit显示到后缀--&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">v-if</span>=<span class="string">&quot;param.options.length &lt;= 0&quot;</span> </span></span><br><span class="line"><span class="tag">                              <span class="attr">:label</span>=<span class="string">&quot;param.k&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;param.v&quot;</span> <span class="attr">:suffix</span>=<span class="string">&quot;param.unit || &#x27;&#x27;&quot;</span>/&gt;</span></span><br><span class="line">                <span class="comment">&lt;!--否则，显示下拉选项--&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-select</span> <span class="attr">v-else</span> <span class="attr">:label</span>=<span class="string">&quot;param.k&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;param.v&quot;</span> <span class="attr">:items</span>=<span class="string">&quot;param.options&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br></pre></td></tr></table></figure>



<p>效果：</p>
<p> <img src="assets/1526302470344.png" alt="1526302470344"></p>
<h2 id="5-7-SKU特有属性"><a href="#5-7-SKU特有属性" class="headerlink" title="5.7.SKU特有属性"></a>5.7.SKU特有属性</h2><p>sku特有属性也存在与specifications中，但是我们现在只想展示特有属性，而不是从头遍历一次。因此，我们应该从specifications中把特有规格属性拆分出来独立保存。</p>
<h3 id="5-7-1-筛选特有规格参数"><a href="#5-7-1-筛选特有规格参数" class="headerlink" title="5.7.1.筛选特有规格参数"></a>5.7.1.筛选特有规格参数</h3><p>首先：我们在data中新建一个属性，保存特有的规格参数：</p>
<p> <img src="assets/1526306568822.png" alt="1526306568822"></p>
<p>然后，在查询完成规格模板后，立刻对规格参数进行处理，筛选出特有规格参数，保存到specialSpecs中：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 根据分类查询规格参数</span></span><br><span class="line"><span class="built_in">this</span>.$http.get(<span class="string">&quot;/item/spec/&quot;</span> + <span class="built_in">this</span>.goods.categories[<span class="number">2</span>].id)</span><br><span class="line">    .then(<span class="function">(<span class="params">&#123;data&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 保存全部规格</span></span><br><span class="line">        <span class="built_in">this</span>.specifications = data;</span><br><span class="line">        <span class="comment">// 对特有规格进行筛选</span></span><br><span class="line">        <span class="keyword">const</span> temp = [];</span><br><span class="line">        data.forEach(<span class="function">(<span class="params">&#123;params&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">            params.forEach(<span class="function">(<span class="params">&#123;k, options, <span class="built_in">global</span>&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (!<span class="built_in">global</span>) &#123;</span><br><span class="line">                    temp.push(&#123;</span><br><span class="line">                        k, options,<span class="attr">selected</span>:[]</span><br><span class="line">                    &#125;)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="built_in">this</span>.specialSpecs = temp;</span><br><span class="line">    &#125;)</span><br></pre></td></tr></table></figure>

<p>要注意：我们添加了一个selected属性，用于保存用户填写的信息</p>
<p>查看数据：</p>
<p> <img src="assets/1526306826026.png" alt="1526306826026"></p>
<h3 id="5-7-2-页面渲染SKU属性"><a href="#5-7-2-页面渲染SKU属性" class="headerlink" title="5.7.2.页面渲染SKU属性"></a>5.7.2.页面渲染SKU属性</h3><p>接下来，我们把筛选出的特有规格参数，渲染到SKU页面：</p>
<p>我们的目标效果是这样的：</p>
<p> <img src="assets/1526307835157.png" alt="1526307835157"></p>
<p>可以看到，</p>
<ul>
<li>每一个特有属性自成一组，都包含标题和选项。我们可以使用card达到这个效果。</li>
<li>无options选项的特有属性，展示一个文本框，有options选项的，展示多个checkbox，让用户选择</li>
</ul>
<p>页面代码实现：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--4、SKU属性--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-stepper-content</span> <span class="attr">step</span>=<span class="string">&quot;4&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">class</span>=<span class="string">&quot;mx-auto&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--遍历特有规格参数--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-card</span> <span class="attr">flat</span> <span class="attr">v-for</span>=<span class="string">&quot;spec in specialSpecs&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;spec.k&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--特有参数的标题--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-card-title</span> <span class="attr">class</span>=<span class="string">&quot;subheading&quot;</span>&gt;</span>&#123;&#123;spec.k&#125;&#125;:<span class="tag">&lt;/<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--特有参数的待选项，需要判断是否有options，如果没有，展示文本框，让用户自己输入--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">v-if</span>=<span class="string">&quot;spec.options.length &lt;= 0&quot;</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">:label</span>=<span class="string">&quot;&#x27;输入新的&#x27; + spec.k&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;spec.selected&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--如果有options，需要展示成多个checkbox--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">v-else</span> <span class="attr">class</span>=<span class="string">&quot;container fluid grid-list-xs&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-layout</span> <span class="attr">row</span> <span class="attr">wrap</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">v-checkbox</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">v-for</span>=<span class="string">&quot;o in spec.options&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;o&quot;</span> <span class="attr">class</span>=<span class="string">&quot;flex xs3&quot;</span></span></span><br><span class="line"><span class="tag">                                <span class="attr">:label</span>=<span class="string">&quot;o&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;spec.selected&quot;</span> <span class="attr">:value</span>=<span class="string">&quot;o&quot;</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">v-layout</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-stepper-content</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>我们的实现效果：</p>
<p><img src="assets/1526308151513.png" alt="1526308151513"></p>
<p>测试下，勾选checkbox或填写文本会发生什么：</p>
<p> <img src="assets/1526308483766.png" alt="1526308483766"></p>
<p>看下规格模板的值：</p>
<p> <img src="assets/1526308513342.png" alt="1526308513342"></p>
<h3 id="5-7-3-自由添加或删除文本框"><a href="#5-7-3-自由添加或删除文本框" class="headerlink" title="5.7.3.自由添加或删除文本框"></a>5.7.3.自由添加或删除文本框</h3><p>刚才的实现中，普通文本项只有一个，如果用户想添加更多值就不行。我们需要让用户能够自由添加新的文本框，而且还能删除。</p>
<p>这里有个取巧的方法：</p>
<p>还记得我们初始化 特有规格参数时，新增了一个selected属性吗，用来保存用户填写的值，是一个数组。每当用户新加一个值，该数组的长度就会加1，而初始长度为0</p>
<p>另外，v-for指令有个特殊之处，就在于它可以遍历数字。比如 v-for=”i in 10”，你会得到1~10</p>
<p>因此，我们可以遍历selected的长度，每当我们输入1个文本，selected长度会加1，自然会多出一个文本框。</p>
<p>代码如下：</p>
<p><img src="assets/1526309463115.png" alt="1526309463115"></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-card</span> <span class="attr">flat</span> <span class="attr">v-for</span>=<span class="string">&quot;spec in specialSpecs&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;spec.k&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--特有参数的标题--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card-title</span> <span class="attr">class</span>=<span class="string">&quot;subheading&quot;</span>&gt;</span>&#123;&#123;spec.k&#125;&#125;:<span class="tag">&lt;/<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--特有参数的待选项，需要判断是否有options，如果没有，展示文本框，让用户自己输入--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">v-if</span>=<span class="string">&quot;spec.options.length &lt;= 0&quot;</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">v-for</span>=<span class="string">&quot;i in spec.selected.length+1&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;i&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">:label</span>=<span class="string">&quot;&#x27;输入新的&#x27; + spec.k&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;spec.selected[i-1]&quot;</span> <span class="attr">v-bind:value</span>=<span class="string">&quot;i&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--如果有options，需要展示成多个checkbox--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card-text</span> <span class="attr">v-else</span> <span class="attr">class</span>=<span class="string">&quot;container fluid grid-list-xs&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">v-layout</span> <span class="attr">row</span> <span class="attr">wrap</span> <span class="attr">class</span>=<span class="string">&quot;px-5&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">v-checkbox</span> <span class="attr">color</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">v-for</span>=<span class="string">&quot;o in spec.options&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;o&quot;</span> <span class="attr">class</span>=<span class="string">&quot;flex xs3&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">:label</span>=<span class="string">&quot;o&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;spec.selected&quot;</span> <span class="attr">:value</span>=<span class="string">&quot;o&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">v-layout</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-card-text</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>效果：</p>
<p> <img src="assets/11.gif"></p>
<p>而删除文本框相对就比较简单了，只要在文本框末尾添加一个按钮，添加点击事件即可，代码：</p>
<p> <img src="assets/1526310720065.png" alt="1526310720065"></p>
<p>添加了一些布局样式，以及一个按钮，在点击事件中删除一个值。</p>
<h2 id="5-8-展示SKU列表"><a href="#5-8-展示SKU列表" class="headerlink" title="5.8.展示SKU列表"></a>5.8.展示SKU列表</h2><h3 id="5-8-1-效果预览"><a href="#5-8-1-效果预览" class="headerlink" title="5.8.1.效果预览"></a>5.8.1.效果预览</h3><p>当我们选定SKU的特有属性时，就会对应出不同排列组合的SKU。</p>
<p>举例：</p>
<p><img src="assets/1526311110498.png" alt="1526311110498"></p>
<p>当你选择了上图中的这些选项时：</p>
<ul>
<li>颜色共2种：土豪金，绚丽红</li>
<li>内存共2种：2GB，4GB</li>
<li>机身存储1种：64GB</li>
</ul>
<p>此时会产生多少种SKU呢？ 应该是 2 * 2 * 1 = 4种。</p>
<p>因此，接下来应该由用户来对这4种sku的信息进行详细填写，比如库存和价格等。而多种sku的最佳展示方式，是表格（淘宝、京东都是这么做的），如图：</p>
<p><img src="assets/1526311330409.png" alt="1526311330409"></p>
<p>而且这个表格应该随着用户选择的不同而动态变化。如何实现？</p>
<h3 id="5-8-2-算法：求数组笛卡尔积"><a href="#5-8-2-算法：求数组笛卡尔积" class="headerlink" title="5.8.2.算法：求数组笛卡尔积"></a>5.8.2.算法：求数组笛卡尔积</h3><p>大家看这个结果就能发现，这其实是在求多个数组的笛卡尔积。作为一个程序员，这应该是基本功了吧。</p>
<blockquote>
<p>两个数组笛卡尔积</p>
</blockquote>
<p>假如有两个数组，求笛卡尔积，其基本思路是这样的：</p>
<ul>
<li>在遍历一个数组的同时，遍历另一个数组，然后把元素拼接，放到新数组。</li>
</ul>
<p>示例1：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr1 = [<span class="string">&#x27;1&#x27;</span>,<span class="string">&#x27;2&#x27;</span>,<span class="string">&#x27;3&#x27;</span>];</span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="string">&#x27;a&#x27;</span>,<span class="string">&#x27;b&#x27;</span>,<span class="string">&#x27;c&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = [];</span><br><span class="line"></span><br><span class="line">arr1.forEach(<span class="function"><span class="params">e1</span> =&gt;</span> &#123;</span><br><span class="line">    arr2.forEach(<span class="function"><span class="params">e2</span> =&gt;</span> &#123;</span><br><span class="line">        result.push(e1 + <span class="string">&quot;_&quot;</span> + e2)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(result);</span><br></pre></td></tr></table></figure>

<p>结果：</p>
<p> <img src="assets/1526354069684.png" alt="1526354069684"></p>
<p>完美实现。</p>
<blockquote>
<p>N个数组的笛卡尔积</p>
</blockquote>
<p>如果是N个数组怎么办？</p>
<p>不确定数组数量，代码没有办法写死。该如何处理？</p>
<p>思路：</p>
<ul>
<li>先拿其中两个数组求笛卡尔积</li>
<li>然后把前面运算的结果作为新数组，与第三个数组求笛卡尔积</li>
</ul>
<p>把前两次运算的结果作为第三次运算的参数。大家想到什么了？</p>
<p>没错，之前讲过的一个数组功能：Reduce</p>
<p>reduce函数的声明：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reduce(callback,initvalue)</span><br></pre></td></tr></table></figure>

<p>callback：是一个回调函数。这个callback可以接收2个参数：arg1,arg2</p>
<ul>
<li>arg1代表的上次运算得到的结果</li>
<li>arg2是数组中正要处理的元素</li>
</ul>
<p>initvalue，初始化值。第一次调用callback时把initvalue作为第一个参数，把数组的第一个元素作为第二个参数运算。如果未指定，则第一次运算会把数组的前两个元素作为参数。</p>
<p>reduce会把数组中的元素逐个用这个函数处理，然后把结果作为下一次回调函数的第一个参数，数组下个元素作为第二个参数，以此类推。</p>
<p>因此，我们可以把想要求笛卡尔积的多个数组先放到一个大数组中。形成二维数组。然后再来运算：</p>
<p>示例2：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr1 = [<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;3&#x27;</span>];</span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>];</span><br><span class="line"><span class="comment">// 用来作为运算的二维数组</span></span><br><span class="line"><span class="keyword">const</span> arr3 = [arr1, arr2, [<span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>]]</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = arr3.reduce(<span class="function">(<span class="params">last, el</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> arr = [];</span><br><span class="line">    <span class="comment">// last：上次运算结果</span></span><br><span class="line">    <span class="comment">// el：数组中的当前元素</span></span><br><span class="line">    last.forEach(<span class="function"><span class="params">e1</span> =&gt;</span> &#123;</span><br><span class="line">        el.forEach(<span class="function"><span class="params">e2</span> =&gt;</span> &#123;</span><br><span class="line">            arr.push(e1 + <span class="string">&quot;_&quot;</span> + e2)</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> arr</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(result);</span><br></pre></td></tr></table></figure>

<p>结果：</p>
<p> <img src="assets/1526355132141.png" alt="1526355132141"></p>
<h3 id="5-8-3-算法结合业务"><a href="#5-8-3-算法结合业务" class="headerlink" title="5.8.3.算法结合业务"></a>5.8.3.算法结合业务</h3><p>来看我们的业务逻辑：</p>
<p>首先，我们已经有了一个特有参数的规格模板：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="string">&quot;k&quot;</span>: <span class="string">&quot;机身颜色&quot;</span>,</span><br><span class="line">    <span class="string">&quot;selected&quot;</span>: [<span class="string">&quot;红色&quot;</span>,<span class="string">&quot;黑色&quot;</span>]</span><br><span class="line">  &#125;,</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="string">&quot;k&quot;</span>: <span class="string">&quot;内存&quot;</span>,</span><br><span class="line">    <span class="string">&quot;selected&quot;</span>: [<span class="string">&quot;8GB&quot;</span>,<span class="string">&quot;6GB&quot;</span>]</span><br><span class="line">  &#125;,</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="string">&quot;k&quot;</span>: <span class="string">&quot;机身存储&quot;</span>,</span><br><span class="line">    <span class="string">&quot;selected&quot;</span>: [<span class="string">&quot;64GB&quot;</span>,<span class="string">&quot;256GB&quot;</span>]</span><br><span class="line">  &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>可以看做是一个二维数组。</p>
<p>一维是参数对象。</p>
<p>二维是参数中的selected选项。</p>
<p>我们想要的结果：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;红色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;6GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;64GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;红色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;6GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;256GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;红色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;8GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;64GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;红色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;8GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;256GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;黑色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;6GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;64GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;黑色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;6GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;256GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;黑色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;8GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;64GB&quot;</span>&#125;,</span><br><span class="line">    &#123;<span class="string">&quot;机身颜色&quot;</span>:<span class="string">&quot;黑色&quot;</span>,<span class="string">&quot;内存&quot;</span>:<span class="string">&quot;8GB&quot;</span>,<span class="string">&quot;机身存储&quot;</span>:<span class="string">&quot;256GB&quot;</span>&#125;,</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>思路是这样：</p>
<ul>
<li>我们的启点是一个空的对象数组：<code>[&#123;&#125;]</code>，</li>
<li>然后先与第一个规格求笛卡尔积</li>
<li>然后再把结果与下一个规格求笛卡尔积，依次类推</li>
</ul>
<p>如果：</p>
<p><img src="assets/skus.png" alt="skus."></p>
<p>代码：</p>
<p>我们在Vue中新增一个计算属性，按照上面所讲的逻辑，计算所有规格参数的笛卡尔积</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">computed: &#123;</span><br><span class="line">    skus() &#123;</span><br><span class="line">        <span class="comment">// 过滤掉用户没有填写数据的规格参数</span></span><br><span class="line">        <span class="keyword">const</span> arr = <span class="built_in">this</span>.specialSpecs.filter(<span class="function"><span class="params">s</span> =&gt;</span> s.selected.length &gt; <span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 通过reduce进行累加笛卡尔积</span></span><br><span class="line">        <span class="keyword">return</span> arr.reduce(<span class="function">(<span class="params">last, spec</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> result = [];</span><br><span class="line">            last.forEach(<span class="function"><span class="params">o</span> =&gt;</span> &#123;</span><br><span class="line">                spec.selected.forEach(<span class="function"><span class="params">option</span> =&gt;</span> &#123;</span><br><span class="line">                    <span class="keyword">const</span> obj = &#123;&#125;;</span><br><span class="line">                    <span class="built_in">Object</span>.assign(obj, o);</span><br><span class="line">                    obj[spec.k] = option;</span><br><span class="line">                    result.push(obj);</span><br><span class="line">                &#125;)</span><br><span class="line">            &#125;)</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line">        &#125;, [&#123;&#125;])</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>结果：</p>
<p> <img src="assets/1526356312119.png" alt="1526356312119"></p>
<p>优化：这里生成的是SKU的数组。因此只包含SKU的规格参数是不够的。结合数据库知道，还需要有下面的字段：</p>
<ul>
<li>price：价格</li>
<li>stock：库存</li>
<li>enable：是否启用。虽然笛卡尔积对应了9个SKU，但用户不一定会需要所有的组合，用这个字段进行标记。</li>
<li>images：商品的图片</li>
<li>indexes：特有属性的索引拼接得到的字符串</li>
</ul>
<p>我们需要给生成的每个sku对象添加上述字段，代码修改如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">computed:&#123;</span><br><span class="line">    skus()&#123;</span><br><span class="line">        <span class="comment">// 过滤掉用户没有填写数据的规格参数</span></span><br><span class="line">        <span class="keyword">const</span> arr = <span class="built_in">this</span>.specialSpecs.filter(<span class="function"><span class="params">s</span> =&gt;</span> s.selected.length &gt; <span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 通过reduce进行累加笛卡尔积</span></span><br><span class="line">        <span class="keyword">return</span>  arr.reduce(<span class="function">(<span class="params">last, spec, index</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> result = [];</span><br><span class="line">            last.forEach(<span class="function"><span class="params">o</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; spec.selected.length; i++)&#123;</span><br><span class="line">                    <span class="keyword">const</span> option = spec.selected[i];</span><br><span class="line">                    <span class="keyword">const</span> obj = &#123;&#125;;</span><br><span class="line">                    <span class="built_in">Object</span>.assign(obj, o);</span><br><span class="line">                    obj[spec.k] = option;</span><br><span class="line">                    <span class="comment">// 拼接当前这个特有属性的索引</span></span><br><span class="line">                    obj.indexes = (o.indexes||<span class="string">&#x27;&#x27;</span>) + <span class="string">&#x27;_&#x27;</span>+ i</span><br><span class="line">                    <span class="keyword">if</span>(index === arr.length - <span class="number">1</span>)&#123;</span><br><span class="line">                        <span class="comment">// 如果发现是最后一组，则添加价格、库存等字段</span></span><br><span class="line">                        <span class="built_in">Object</span>.assign(obj, &#123; <span class="attr">price</span>:<span class="number">0</span>, <span class="attr">stock</span>:<span class="number">0</span>,<span class="attr">enable</span>:<span class="literal">false</span>, <span class="attr">images</span>:[]&#125;)</span><br><span class="line">                        <span class="comment">// 去掉索引字符串开头的下划线</span></span><br><span class="line">                        obj.indexes = obj.indexes.substring(<span class="number">1</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                    result.push(obj);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)</span><br><span class="line">            <span class="keyword">return</span> result</span><br><span class="line">        &#125;,[&#123;&#125;])</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>查看生成的数据：</p>
<p> <img src="assets/1526363824808.png" alt="1526363824808"></p>
<h3 id="5-8-4-页面展现"><a href="#5-8-4-页面展现" class="headerlink" title="5.8.4.页面展现"></a>5.8.4.页面展现</h3><p>页面展现是一个表格。我们之前已经用过。表格需要以下信息：</p>
<ul>
<li>items：表格内的数据</li>
<li>headers：表头信息</li>
</ul>
<p>刚才我们的计算属性skus得到的就是表格数据了。我们还差头：headers</p>
<p>头部信息也是动态的，用户选择了一个属性，就会多出一个表头。与skus是关联的。</p>
<p>既然如此，我们再次编写一个计算属性，来计算得出header数组：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">headers()&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.skus.length &lt;= <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> []</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> headers = [];</span><br><span class="line">    <span class="comment">// 获取skus中的任意一个，获取key，然后遍历其属性</span></span><br><span class="line">    <span class="built_in">Object</span>.keys(<span class="built_in">this</span>.skus[<span class="number">0</span>]).forEach(<span class="function"><span class="params">k</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> value = k;</span><br><span class="line">        <span class="keyword">if</span>(k === <span class="string">&#x27;price&#x27;</span>)&#123;</span><br><span class="line">            <span class="comment">// enable，表头要翻译成“价格”</span></span><br><span class="line">            k = <span class="string">&#x27;价格&#x27;</span></span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(k === <span class="string">&#x27;stock&#x27;</span>)&#123;</span><br><span class="line">            <span class="comment">// enable，表头要翻译成“库存”</span></span><br><span class="line">            k = <span class="string">&#x27;库存&#x27;</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(k === <span class="string">&#x27;enable&#x27;</span>)&#123;</span><br><span class="line">            <span class="comment">// enable，表头要翻译成“是否启用”</span></span><br><span class="line">            k = <span class="string">&#x27;是否启用&#x27;</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span>(k === <span class="string">&#x27;indexes&#x27;</span> || k === <span class="string">&#x27;images&#x27;</span>)&#123;</span><br><span class="line">            <span class="comment">// 图片和索引不在表格中展示</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        headers.push(&#123;</span><br><span class="line">            text: k,</span><br><span class="line">            align: <span class="string">&#x27;center&#x27;</span>,</span><br><span class="line">            sortable: <span class="literal">false</span>,</span><br><span class="line">            value</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> headers;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>接下来编写页面，实现table。</p>
<p>需要注意的是，price、stock字段需要用户填写数值，不能直接展示。enable要展示为checkbox，让用户选择，如图：</p>
<p><img src="assets/1526364758744.png" alt="1526364758744"></p>
<p>代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">v-card</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--标题--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-card-title</span> <span class="attr">class</span>=<span class="string">&quot;subheading&quot;</span>&gt;</span>SKU列表<span class="tag">&lt;/<span class="name">v-card-title</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--SKU表格，hide-actions因此分页等工具条--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-data-table</span> <span class="attr">:items</span>=<span class="string">&quot;skus&quot;</span> <span class="attr">:headers</span>=<span class="string">&quot;headers&quot;</span> <span class="attr">hide-actions</span> <span class="attr">item-key</span>=<span class="string">&quot;indexes&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">&quot;items&quot;</span> <span class="attr">slot-scope</span>=<span class="string">&quot;props&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--价格和库存展示为文本框--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">td</span> <span class="attr">v-for</span>=<span class="string">&quot;(v,k) in props.item&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;k&quot;</span> <span class="attr">v-if</span>=<span class="string">&quot;[&#x27;price&#x27;, &#x27;stock&#x27;].includes(k)&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-text-field</span> <span class="attr">single-line</span> <span class="attr">v-model.number</span>=<span class="string">&quot;props.item[k]&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--enable展示为checkbox--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span> <span class="attr">v-else-if</span>=<span class="string">&quot;k === &#x27;enable&#x27;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">v-checkbox</span> <span class="attr">v-model</span>=<span class="string">&quot;props.item[k]&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--indexes和images不展示，其它展示为普通文本--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">td</span> <span class="attr">class</span>=<span class="string">&quot;text-xs-center&quot;</span> <span class="attr">v-else-if</span>=<span class="string">&quot;![&#x27;indexes&#x27;,&#x27;images&#x27;].includes(k)&quot;</span>&gt;</span>&#123;&#123;v&#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">v-data-table</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-card</span>&gt;</span></span><br></pre></td></tr></table></figure>







<p>效果：</p>
<p><img src="assets/1526365039421.png" alt="1526365039421"></p>
<h3 id="5-8-5-图片上传列表"><a href="#5-8-5-图片上传列表" class="headerlink" title="5.8.5.图片上传列表"></a>5.8.5.图片上传列表</h3><p>这个表格中只展示了基本信息，当用户需要上传图片时，该怎么做呢？</p>
<p>Vuetify的table有一个展开功能，可以提供额外的展示空间：</p>
<p><img src="assets/1526366340741.png" alt="1526366340741"></p>
<p>用法也非常简单，添加一个template，把其slot属性指定为expand即可：</p>
<p><img src="assets/1526366667064.png" alt="1526366667064"></p>
<p>效果：</p>
<p><img src="assets/1526366997943.png" alt="1526366997943"></p>
<p>接下来就是我们的图片上传组件：v-upload</p>
<p><img src="assets/1526367719809.png" alt="1526367719809"></p>
<h2 id="5-9-表单提交"><a href="#5-9-表单提交" class="headerlink" title="5.9.表单提交"></a>5.9.表单提交</h2><h3 id="5-9-1-添加提交按钮"><a href="#5-9-1-添加提交按钮" class="headerlink" title="5.9.1.添加提交按钮"></a>5.9.1.添加提交按钮</h3><p>我们在step=4，也就是SKU属性列表页面， 添加一个提交按钮。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--提交按钮--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">xs3</span> <span class="attr">offset-xs9</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;info&quot;</span>&gt;</span>保存商品信息<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>效果：</p>
<p><img src="assets/1526366187195.png" alt="1526366187195"></p>
<h3 id="5-9-2点击事件"><a href="#5-9-2点击事件" class="headerlink" title="5.9.2点击事件"></a>5.9.2点击事件</h3><p>当用户点击保存，我们就需要对页面的数据进行整理，然后提交到后台服务。</p>
<p>现在我们页面包含了哪些信息呢？我们与数据库对比，看看少什么</p>
<ul>
<li>goods：里面包含了SPU的几乎所有信息<ul>
<li>title：标题</li>
<li>subtitle：子标题，卖点</li>
<li>categories：分类对象数组，需要进行整理 **</li>
<li>brandId：品牌id</li>
<li>spuDetail：商品详情<ul>
<li>packingList：包装清单</li>
<li>afterService：售后服务</li>
<li>description：商品描述</li>
<li>缺少全局规格属性specifications **</li>
<li>缺少特有规格属性模板spec_template **</li>
</ul>
</li>
</ul>
</li>
<li>skus：包含了sku列表的几乎所有信息<ul>
<li>price：价格，需要处理为以分为单位</li>
<li>stock：库存</li>
<li>enable：是否启用</li>
<li>indexes：索引</li>
<li>images：图片，数组，需要处理为字符串**</li>
<li>缺少其它特有规格，ows_spec **</li>
<li>缺少标题：需要根据spu的标题结合特有属性生成 ** </li>
</ul>
</li>
<li>specifications：全局规格参数的键值对信息</li>
<li>specialSpec：特有规格参数信息</li>
</ul>
<p>在页面绑定点击事件：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--提交按钮--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">v-flex</span> <span class="attr">xs3</span> <span class="attr">offset-xs9</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">v-btn</span> <span class="attr">color</span>=<span class="string">&quot;info&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>保存商品信息<span class="tag">&lt;/<span class="name">v-btn</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">v-flex</span>&gt;</span></span><br></pre></td></tr></table></figure>





<p>编写代码，整理数据：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">submit()&#123;</span><br><span class="line">    <span class="comment">// 表单校验。 略</span></span><br><span class="line">    <span class="comment">// 先处理goods，用结构表达式接收,除了categories外，都接收到goodsParams中</span></span><br><span class="line">    <span class="keyword">const</span> &#123;<span class="attr">categories</span>: [&#123;<span class="attr">id</span>:cid1&#125;,&#123;<span class="attr">id</span>:cid2&#125;,&#123;<span class="attr">id</span>:cid3&#125;], ...goodsParams&#125; = <span class="built_in">this</span>.goods;</span><br><span class="line">    <span class="comment">// 处理规格参数</span></span><br><span class="line">    <span class="keyword">const</span> specs = <span class="built_in">this</span>.specifications.map(<span class="function">(<span class="params">&#123;group,params&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> newParams = params.map(<span class="function">(<span class="params">&#123;options,...rest&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> rest;</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">return</span> &#123;group,<span class="attr">params</span>:newParams&#125;;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 处理特有规格参数模板</span></span><br><span class="line">    <span class="keyword">const</span> specTemplate = &#123;&#125;;</span><br><span class="line">    <span class="built_in">this</span>.specialSpecs.forEach(<span class="function">(<span class="params">&#123;k, selected&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        specTemplate[k] = selected;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 处理sku</span></span><br><span class="line">    <span class="keyword">const</span> skus = <span class="built_in">this</span>.skus.filter(<span class="function"><span class="params">s</span> =&gt;</span> s.enable).map(<span class="function">(<span class="params">&#123;price,stock,enable,images,indexes, ...rest&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 标题，在spu的title基础上，拼接特有规格属性值</span></span><br><span class="line">        <span class="keyword">const</span> title = goodsParams.title + <span class="string">&quot; &quot;</span> + <span class="built_in">Object</span>.values(rest).join(<span class="string">&quot; &quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            price: <span class="built_in">this</span>.$format(price+<span class="string">&quot;&quot;</span>),stock,enable,indexes,title,<span class="comment">// 基本属性</span></span><br><span class="line">            images: !images ? <span class="string">&#x27;&#x27;</span> : images.join(<span class="string">&quot;,&quot;</span>), <span class="comment">// 图片</span></span><br><span class="line">            ownSpec: <span class="built_in">JSON</span>.stringify(rest), <span class="comment">// 特有规格参数</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="built_in">Object</span>.assign(goodsParams, &#123;</span><br><span class="line">        cid1,cid2,cid3, <span class="comment">// 商品分类</span></span><br><span class="line">        skus, <span class="comment">// sku列表</span></span><br><span class="line">    &#125;)</span><br><span class="line">    goodsParams.spuDetail.specifications= <span class="built_in">JSON</span>.stringify(specs);</span><br><span class="line">    goodsParams.spuDetail.specTemplate = <span class="built_in">JSON</span>.stringify(specTemplate);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">console</span>.log(goodsParams)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>点击测试，看效果：</p>
<p><img src="assets/1526391630336.png" alt="1526391630336"></p>
<p>向后台发起请求，因为请求体复杂，我们直接发起Json请求：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>.$http.post(<span class="string">&quot;/item/goods&quot;</span>,goodsParams)</span><br><span class="line">    .then(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 成功，关闭窗口</span></span><br><span class="line">    <span class="built_in">this</span>.$emit(<span class="string">&#x27;close&#x27;</span>);</span><br><span class="line">    <span class="comment">// 提示成功</span></span><br><span class="line">    <span class="built_in">this</span>.$message.success(<span class="string">&quot;新增成功了&quot;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">    .catch(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.$message.error(<span class="string">&quot;保存失败！&quot;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<h3 id="5-9-3-后台编写接口"><a href="#5-9-3-后台编写接口" class="headerlink" title="5.9.3.后台编写接口"></a>5.9.3.后台编写接口</h3><h4 id="实体类"><a href="#实体类" class="headerlink" title="实体类"></a>实体类</h4><blockquote>
<p>Spu</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_spu&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Spu</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> Long brandId;</span><br><span class="line">    <span class="keyword">private</span> Long cid1;<span class="comment">// 1级类目</span></span><br><span class="line">    <span class="keyword">private</span> Long cid2;<span class="comment">// 2级类目</span></span><br><span class="line">    <span class="keyword">private</span> Long cid3;<span class="comment">// 3级类目</span></span><br><span class="line">    <span class="keyword">private</span> String title;<span class="comment">// 标题</span></span><br><span class="line">    <span class="keyword">private</span> String subTitle;<span class="comment">// 子标题</span></span><br><span class="line">    <span class="keyword">private</span> Boolean saleable;<span class="comment">// 是否上架</span></span><br><span class="line">    <span class="keyword">private</span> Boolean valid;<span class="comment">// 是否有效，逻辑删除用</span></span><br><span class="line">    <span class="keyword">private</span> Date createTime;<span class="comment">// 创建时间</span></span><br><span class="line">    <span class="keyword">private</span> Date lastUpdateTime;<span class="comment">// 最后修改时间</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>SpuDetail</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name=&quot;tb_spu_detail&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpuDetail</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="keyword">private</span> Long spuId;<span class="comment">// 对应的SPU的id</span></span><br><span class="line">    <span class="keyword">private</span> String description;<span class="comment">// 商品描述</span></span><br><span class="line">    <span class="keyword">private</span> String specTemplate;<span class="comment">// 商品特殊规格的名称及可选值模板</span></span><br><span class="line">    <span class="keyword">private</span> String specifications;<span class="comment">// 商品的全局规格属性</span></span><br><span class="line">    <span class="keyword">private</span> String packingList;<span class="comment">// 包装清单</span></span><br><span class="line">    <span class="keyword">private</span> String afterService;<span class="comment">// 售后服务</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>Sku</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_sku&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Sku</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="keyword">private</span> Long spuId;</span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    <span class="keyword">private</span> String images;</span><br><span class="line">    <span class="keyword">private</span> Long price;</span><br><span class="line">    <span class="keyword">private</span> String ownSpec;<span class="comment">// 商品特殊规格的键值对</span></span><br><span class="line">    <span class="keyword">private</span> String indexes;<span class="comment">// 商品特殊规格的下标</span></span><br><span class="line">    <span class="keyword">private</span> Boolean enable;<span class="comment">// 是否有效，逻辑删除用</span></span><br><span class="line">    <span class="keyword">private</span> Date createTime;<span class="comment">// 创建时间</span></span><br><span class="line">    <span class="keyword">private</span> Date lastUpdateTime;<span class="comment">// 最后修改时间</span></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    <span class="keyword">private</span> Long stock;<span class="comment">// 库存</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意：这里保存了一个库存字段，在数据库中是另外一张表保存的，方便查询。</p>
<blockquote>
<p>Stock</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Table(name = &quot;tb_stock&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Stock</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="keyword">private</span> Long skuId;</span><br><span class="line">    <span class="keyword">private</span> Integer seckillStock;<span class="comment">// 秒杀可用库存</span></span><br><span class="line">    <span class="keyword">private</span> Integer seckillTotal;<span class="comment">// 已秒杀数量</span></span><br><span class="line">    <span class="keyword">private</span> Integer stock;<span class="comment">// 正常库存</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="Controller"><a href="#Controller" class="headerlink" title="Controller"></a>Controller</h4><p>四个问题：</p>
<ul>
<li><p>请求方式：POST</p>
</li>
<li><p>请求路径：/goods</p>
</li>
<li><p>请求参数：Spu的json格式的对象，spu中包含spuDetail和Sku集合。这里我们该怎么接收？我们之前定义了一个SpuBo对象，作为业务对象。这里也可以用它，不过需要再扩展spuDetail和skus字段：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpuBo</span> <span class="keyword">extends</span> <span class="title">Spu</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    String cname;<span class="comment">// 商品分类名称</span></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    String bname;<span class="comment">// 品牌名称</span></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    SpuDetail spuDetail;<span class="comment">// 商品详情</span></span><br><span class="line">    <span class="meta">@Transient</span></span><br><span class="line">    List&lt;Sku&gt; skus;<span class="comment">// sku列表</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>返回类型：无</p>
</li>
</ul>
<p>代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 新增商品</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> spu</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PostMapping</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ResponseEntity&lt;Void&gt; <span class="title">saveGoods</span><span class="params">(<span class="meta">@RequestBody</span> Spu spu)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.goodsService.save(spu);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity&lt;&gt;(HttpStatus.CREATED);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity&lt;&gt;(HttpStatus.INTERNAL_SERVER_ERROR);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意：通过@RequestBody注解来接收Json请求</p>
<blockquote>
<p>Service</p>
</blockquote>
<p>这里的逻辑比较复杂，我们除了要对SPU新增以外，还要对SpuDetail、Sku、Stock进行保存</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">save</span><span class="params">(SpuBo spu)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 保存spu</span></span><br><span class="line">    spu.setSaleable(<span class="keyword">true</span>);</span><br><span class="line">    spu.setValid(<span class="keyword">true</span>);</span><br><span class="line">    spu.setCreateTime(<span class="keyword">new</span> Date());</span><br><span class="line">    spu.setLastUpdateTime(spu.getCreateTime());</span><br><span class="line">    <span class="keyword">this</span>.spuMapper.insert(spu);</span><br><span class="line">    <span class="comment">// 保存spu详情</span></span><br><span class="line">    spu.getSpuDetail().setSpuId(spu.getId());</span><br><span class="line">    <span class="keyword">this</span>.spuDetailMapper.insert(spu.getSpuDetail());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 保存sku和库存信息</span></span><br><span class="line">    saveSkuAndStock(spu.getSkus(), spu.getId());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">saveSkuAndStock</span><span class="params">(List&lt;Sku&gt; skus, Long spuId)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (Sku sku : skus) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!sku.getEnable()) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 保存sku</span></span><br><span class="line">        sku.setSpuId(spuId);</span><br><span class="line">        <span class="comment">// 默认不参与任何促销</span></span><br><span class="line">        sku.setCreateTime(<span class="keyword">new</span> Date());</span><br><span class="line">        sku.setLastUpdateTime(sku.getCreateTime());</span><br><span class="line">        <span class="keyword">this</span>.skuMapper.insert(sku);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 保存库存信息</span></span><br><span class="line">        Stock stock = <span class="keyword">new</span> Stock();</span><br><span class="line">        stock.setSkuId(sku.getId());</span><br><span class="line">        stock.setStock(sku.getStock());</span><br><span class="line">        <span class="keyword">this</span>.stockMapper.insert(stock);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="Mapper"><a href="#Mapper" class="headerlink" title="Mapper"></a>Mapper</h4><p>都是通用Mapper，略</p>

            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        Author:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">AverageJoe</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        Link:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="http://www.averagejoe.top/2018/08/19/leyou/day09-bi-ji/day09-shang-pin-gui-ge-guan-li/">http://www.averagejoe.top/2018/08/19/leyou/day09-bi-ji/day09-shang-pin-gui-ge-guan-li/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        Reprint policy:
                    </i>
                </span>
                <span class="reprint-info">
                    All articles in this blog are used except for special statements
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    reprint polocy. If reproduced, please indicate source
                    <a href="/about" target="_blank">AverageJoe</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>Copied successfully, please follow the reprint policy of this article</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">more</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            <span class="chip bg-color">No tag</span>
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    
        <style>
    .valine-card {
        margin: 1.5rem auto;
    }

    .valine-card .card-content {
        padding: 20px 20px 5px 20px;
    }

    #vcomments textarea {
        box-sizing: border-box;
        background: url("/medias/comment_bg.png") 100% 100% no-repeat;
    }

    #vcomments p {
        margin: 2px 2px 10px;
        font-size: 1.05rem;
        line-height: 1.78rem;
    }

    #vcomments blockquote p {
        text-indent: 0.2rem;
    }

    #vcomments a {
        padding: 0 2px;
        color: #4cbf30;
        font-weight: 500;
        text-decoration: none;
    }

    #vcomments img {
        max-width: 100%;
        height: auto;
        cursor: pointer;
    }

    #vcomments ol li {
        list-style-type: decimal;
    }

    #vcomments ol,
    ul {
        display: block;
        padding-left: 2em;
        word-spacing: 0.05rem;
    }

    #vcomments ul li,
    ol li {
        display: list-item;
        line-height: 1.8rem;
        font-size: 1rem;
    }

    #vcomments ul li {
        list-style-type: disc;
    }

    #vcomments ul ul li {
        list-style-type: circle;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    #vcomments table, th, td {
        border: 0;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments h1 {
        font-size: 1.85rem;
        font-weight: bold;
        line-height: 2.2rem;
    }

    #vcomments h2 {
        font-size: 1.65rem;
        font-weight: bold;
        line-height: 1.9rem;
    }

    #vcomments h3 {
        font-size: 1.45rem;
        font-weight: bold;
        line-height: 1.7rem;
    }

    #vcomments h4 {
        font-size: 1.25rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    #vcomments h5 {
        font-size: 1.1rem;
        font-weight: bold;
        line-height: 1.4rem;
    }

    #vcomments h6 {
        font-size: 1rem;
        line-height: 1.3rem;
    }

    #vcomments p {
        font-size: 1rem;
        line-height: 1.5rem;
    }

    #vcomments hr {
        margin: 12px 0;
        border: 0;
        border-top: 1px solid #ccc;
    }

    #vcomments blockquote {
        margin: 15px 0;
        border-left: 5px solid #42b983;
        padding: 1rem 0.8rem 0.3rem 0.8rem;
        color: #666;
        background-color: rgba(66, 185, 131, .1);
    }

    #vcomments pre {
        font-family: monospace, monospace;
        padding: 1.2em;
        margin: .5em 0;
        background: #272822;
        overflow: auto;
        border-radius: 0.3em;
        tab-size: 4;
    }

    #vcomments code {
        font-family: monospace, monospace;
        padding: 1px 3px;
        font-size: 0.92rem;
        color: #e96900;
        background-color: #f8f8f8;
        border-radius: 2px;
    }

    #vcomments pre code {
        font-family: monospace, monospace;
        padding: 0;
        color: #e8eaf6;
        background-color: #272822;
    }

    #vcomments pre[class*="language-"] {
        padding: 1.2em;
        margin: .5em 0;
    }

    #vcomments code[class*="language-"],
    pre[class*="language-"] {
        color: #e8eaf6;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }

    #vcomments b,
    strong {
        font-weight: bold;
    }

    #vcomments dfn {
        font-style: italic;
    }

    #vcomments small {
        font-size: 85%;
    }

    #vcomments cite {
        font-style: normal;
    }

    #vcomments mark {
        background-color: #fcf8e3;
        padding: .2em;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }
</style>

<div class="card valine-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="vcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/valine/av-min.js"></script>
<script src="/libs/valine/Valine.min.js"></script>
<script>
    new Valine({
        el: '#vcomments',
        appId: 'BhOC9gMKAfPsRTjqr679tX7A-gzGzoHsz',
        appKey: 'xb9C1I9k62m6AJvin0UWGhSr',
        notify: 'false' === 'true',
        verify: 'false' === 'true',
        visitor: 'true' === 'true',
        avatar: 'mm',
        pageSize: '10',
        lang: 'en',
        placeholder: 'just go go'
    });
</script>

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;Previous</div>
            <div class="card">
                <a href="/2018/08/19/leyou/day08-bi-ji/day08-pin-pai-guan-li/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/0.jpg" class="responsive-img" alt="">
                        
                        <span class="card-title"></span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            0.学习目标
独立实现品牌新增
实现图片上传
了解FastDFS的安装
使用FastDFS客户端实现上传

1.品牌的新增昨天我们完成了品牌的查询，接下来就是新增功能。
1.1.页面实现1.1.1.初步编写弹窗当我们点击新增按钮，应该出现一
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2018-08-19
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-user fa-fw"></i>
                            AverageJoe
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                Next&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2018/08/18/leyou/day19-bi-ji/day19-xia-dan/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/0.jpg" class="responsive-img" alt="">
                        
                        <span class="card-title"></span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            0.学习目标
会调用订单系统接口
实现订单结算功能
实现微信支付功能

1.订单系统接口我们不做开发，只讲解
1.1.导入订单服务把课前资料提供的leyou-order复制到D:\heima\code\leyou目录。
然后在工程内导入：

                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2018-08-18
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-user fa-fw"></i>
                            AverageJoe
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


<!-- 代码块折行 -->

<style type="text/css">
code[class*="language-"], pre[class*="language-"] { white-space: pre !important; }
</style>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;TOC</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h2, h3, h4').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        display: none;
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        display: none;
        
        font-size: 15px;
        color: #42b983;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="playlist"
                   id="503838841"
                   fixed='true'
                   autoplay='false'
                   theme='#42b983'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/libs/aplayer/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>

    
    <div class="container row center-align" style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            <span id="year">2019</span>
            <a href="/about" target="_blank">AverageJoe</a>
            |&nbsp;Powered by&nbsp;<a href="#" target="_blank">AverageJoe</a>
            |&nbsp;Theme&nbsp;<a href="#" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">213.1k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">


    <a href="mailto:1533360044@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1533360044" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1533360044" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;Search</span>
            <input type="search" id="searchInput" name="s" placeholder="Please enter a search keyword"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
$(function () {
    searchFunc("/search.xml", 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    
	

	<div id="weather-v2-plugin-simple"></div>
<script>
WIDGET = {
  CONFIG: {
    "modules": "0124",
    "background": 1,
    "tmpColor": "FFFFFF",
    "tmpSize": 16,
    "cityColor": "FFFFFF",
    "citySize": 16,
    "aqiSize": 16,
    "weatherIconSize": 24,
    "alertIconSize": 18,
    "padding": "10px 10px 10px 10px",
    "shadow": "1",
    "language": "auto",
    "borderRadius": 5,
    "fixed": "false",
    "vertical": "middle",
    "horizontal": "center",
    "key": "XZgo8dbNqo"
  }
}
</script>
<script src="https://apip.weatherdt.com/simple/static/js/weather-simple-common.js?v=2.0"></script>
<script src="/js/cursor.js"></script>
</body>

</html>
